From b1dc519917cb441161dbe5db2d70b7958a0a068e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AE=E7=94=9F=E8=8B=A5=E6=A2=A6?= <1070753498@qq.com> Date: Thu, 29 Feb 2024 18:40:35 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84transcode-3=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- examples/player/CMakeLists.txt | 13 +- examples/player/main.cpp | 2 +- examples/player/player.pro | 2 +- examples/transcoder/CMakeLists.txt | 23 +- examples/transcoder/audioencoderwidget.cc | 64 ++++ examples/transcoder/audioencoderwidget.hpp | 28 ++ examples/transcoder/main.cc | 2 +- examples/transcoder/mainwindow.cc | 325 ++++----------------- examples/transcoder/mainwindow.hpp | 2 - examples/transcoder/outputwidget.cc | 64 ++++ examples/transcoder/outputwidget.hpp | 27 ++ examples/transcoder/previewwidget.cc | 1 + examples/transcoder/sourcewidget.cc | 12 +- examples/transcoder/stautuswidget.cc | 75 +++++ examples/transcoder/stautuswidget.hpp | 33 +++ examples/transcoder/transcoder.pro | 14 +- examples/transcoder/videoencoderwidget.cc | 293 +++++++++++++++++++ examples/transcoder/videoencoderwidget.hpp | 54 ++++ ffmpeg/codeccontext.cpp | 4 +- ffmpeg/ffmpegutils.cc | 4 +- ffmpeg/transcoder.cc | 17 +- ffmpeg/transcoder.hpp | 4 +- 23 files changed, 759 insertions(+), 308 deletions(-) create mode 100644 examples/transcoder/audioencoderwidget.cc create mode 100644 examples/transcoder/audioencoderwidget.hpp create mode 100644 examples/transcoder/outputwidget.cc create mode 100644 examples/transcoder/outputwidget.hpp create mode 100644 examples/transcoder/stautuswidget.cc create mode 100644 examples/transcoder/stautuswidget.hpp create mode 100644 examples/transcoder/videoencoderwidget.cc create mode 100644 examples/transcoder/videoencoderwidget.hpp diff --git a/README.md b/README.md index f3ff6b2..080f2ed 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - [简体中文](README.md) - [English](README.en.md) -## QFfmpegPlayer +## Player
@@ -56,7 +56,7 @@ Dialogue: 0,0:01:06.77,0:01:08.00,en,,0000,0000,0000,,Peek-a-boo!\r\n subtitles=filename='%1':original_size=%2x%3 ``` -## QFfmpegTranscoder +## Transcoder 如何设置编码参数以获得更小的文件和更好的视频质量? diff --git a/examples/player/CMakeLists.txt b/examples/player/CMakeLists.txt index 392ac7e..c875166 100644 --- a/examples/player/CMakeLists.txt +++ b/examples/player/CMakeLists.txt @@ -22,14 +22,13 @@ set(PROJECT_SOURCES titlewidget.cc titlewidget.hpp) -qt_add_executable(QFfmpegPlayer MANUAL_FINALIZATION ${PROJECT_SOURCES}) -target_link_libraries( - QFfmpegPlayer PRIVATE ffmpeg thirdparty utils Qt6::Widgets Qt6::Multimedia - Qt6::OpenGLWidgets) -target_link_libraries(QFfmpegPlayer PRIVATE PkgConfig::ffmpeg) +qt_add_executable(Player MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_link_libraries(Player PRIVATE ffmpeg thirdparty utils Qt6::Widgets + Qt6::Multimedia Qt6::OpenGLWidgets) +target_link_libraries(Player PRIVATE PkgConfig::ffmpeg) if(CMAKE_HOST_APPLE) target_link_libraries( - QFfmpegPlayer + Player PRIVATE ${Foundation_LIBRARY} ${CoreAudio_LIBRARY} ${AVFoundation_LIBRARY} @@ -46,4 +45,4 @@ if(CMAKE_HOST_APPLE) ${CoreVideo_LIBRARY} ${CoreServices_LIBRARY}) endif() -qt_finalize_executable(QFfmpegPlayer) +qt_finalize_executable(Player) diff --git a/examples/player/main.cpp b/examples/player/main.cpp index 8d34b3b..acc1480 100644 --- a/examples/player/main.cpp +++ b/examples/player/main.cpp @@ -11,7 +11,7 @@ #include #include -#define AppName "QFfmpegPlayer" +#define AppName "Player" void setAppInfo() { diff --git a/examples/player/player.pro b/examples/player/player.pro index f923a5d..a923210 100644 --- a/examples/player/player.pro +++ b/examples/player/player.pro @@ -4,7 +4,7 @@ QT += core gui widgets network multimedia openglwidgets core5compat TEMPLATE = app -TARGET = QFfmpegPlayer +TARGET = Player LIBS += \ -L$$APP_OUTPUT_PATH/../libs \ diff --git a/examples/transcoder/CMakeLists.txt b/examples/transcoder/CMakeLists.txt index f2162ae..1bf27b3 100644 --- a/examples/transcoder/CMakeLists.txt +++ b/examples/transcoder/CMakeLists.txt @@ -1,21 +1,28 @@ set(PROJECT_SOURCES + audioencoderwidget.cc + audioencoderwidget.hpp main.cc mainwindow.cc mainwindow.hpp + outputwidget.cc + outputwidget.hpp previewwidget.cc previewwidget.hpp sourcewidget.cc - sourcewidget.hpp) + sourcewidget.hpp + stautuswidget.cc + stautuswidget.hpp + videoencoderwidget.cc + videoencoderwidget.hpp) -qt_add_executable(QFfmpegTranscoder MANUAL_FINALIZATION ${PROJECT_SOURCES}) -target_link_libraries( - QFfmpegTranscoder PRIVATE ffmpeg thirdparty utils Qt6::Widgets - Qt6::Multimedia Qt6::OpenGLWidgets) -target_link_libraries(QFfmpegTranscoder PRIVATE PkgConfig::ffmpeg) +qt_add_executable(Transcoder MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_link_libraries(Transcoder PRIVATE ffmpeg thirdparty utils Qt6::Widgets + Qt6::Multimedia Qt6::OpenGLWidgets) +target_link_libraries(Transcoder PRIVATE PkgConfig::ffmpeg) if(CMAKE_HOST_APPLE) target_link_libraries( - QFfmpegTranscoder + Transcoder PRIVATE ${Foundation_LIBRARY} ${CoreAudio_LIBRARY} ${AVFoundation_LIBRARY} @@ -33,4 +40,4 @@ if(CMAKE_HOST_APPLE) ${CoreServices_LIBRARY}) endif() -qt_finalize_executable(QFfmpegTranscoder) +qt_finalize_executable(Transcoder) diff --git a/examples/transcoder/audioencoderwidget.cc b/examples/transcoder/audioencoderwidget.cc new file mode 100644 index 0000000..1ca1d2f --- /dev/null +++ b/examples/transcoder/audioencoderwidget.cc @@ -0,0 +1,64 @@ +#include "audioencoderwidget.hpp" + +#include + +#include + +class AudioEncoderWidget::AudioEncoderWidgetPrivate +{ +public: + explicit AudioEncoderWidgetPrivate(AudioEncoderWidget *q) + : q_ptr(q) + { + audioEncoderCbx = new QComboBox(q_ptr); + audioEncoderCbx->setView(new QListView(audioEncoderCbx)); + audioEncoderCbx->setMaxVisibleItems(10); + audioEncoderCbx->setStyleSheet("QComboBox {combobox-popup:0;}"); + + auto audioEncodercs = Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_AUDIO, true); + for (auto iter = audioEncodercs.cbegin(); iter != audioEncodercs.cend(); ++iter) { + audioEncoderCbx->addItem(iter.value(), iter.key()); + } + audioEncoderCbx->setCurrentIndex(audioEncoderCbx->findData(AV_CODEC_ID_AAC)); + audioEncoderCbx->model()->sort(0); + } + + AudioEncoderWidget *q_ptr; + + QComboBox *audioEncoderCbx; +}; + +AudioEncoderWidget::AudioEncoderWidget(QWidget *parent) + : QWidget{parent} + , d_ptr(new AudioEncoderWidgetPrivate(this)) +{ + setupUI(); + buildConnect(); +} + +AudioEncoderWidget::~AudioEncoderWidget() = default; + +auto AudioEncoderWidget::setEncoder(AVCodecID codecId) -> bool +{ + auto index = d_ptr->audioEncoderCbx->findData(codecId); + auto finded = (index >= 0); + if (finded) { + d_ptr->audioEncoderCbx->setCurrentIndex(index); + } + return finded; +} + +auto AudioEncoderWidget::encoder() const -> QString +{ + return d_ptr->audioEncoderCbx->currentText(); +} + +void AudioEncoderWidget::setupUI() +{ + auto *layout = new QHBoxLayout(this); + layout->addWidget(new QLabel(tr("Encoder:"))); + layout->addWidget(d_ptr->audioEncoderCbx); + layout->addStretch(); +} + +void AudioEncoderWidget::buildConnect() {} diff --git a/examples/transcoder/audioencoderwidget.hpp b/examples/transcoder/audioencoderwidget.hpp new file mode 100644 index 0000000..da98530 --- /dev/null +++ b/examples/transcoder/audioencoderwidget.hpp @@ -0,0 +1,28 @@ +#ifndef AUDIOENCODERWIDGET_HPP +#define AUDIOENCODERWIDGET_HPP + +#include + +extern "C" { +#include +} + +class AudioEncoderWidget : public QWidget +{ + Q_OBJECT +public: + explicit AudioEncoderWidget(QWidget *parent = nullptr); + ~AudioEncoderWidget() override; + + auto setEncoder(AVCodecID codecId) -> bool; + [[nodiscard]] auto encoder() const -> QString; + +private: + void setupUI(); + void buildConnect(); + + class AudioEncoderWidgetPrivate; + QScopedPointer d_ptr; +}; + +#endif // AUDIOENCODERWIDGET_HPP diff --git a/examples/transcoder/main.cc b/examples/transcoder/main.cc index fe92f34..ad22f24 100644 --- a/examples/transcoder/main.cc +++ b/examples/transcoder/main.cc @@ -10,7 +10,7 @@ #include #include -#define AppnName "QFfmpegTranscoder" +#define AppnName "Transcoder" void setAppInfo() { diff --git a/examples/transcoder/mainwindow.cc b/examples/transcoder/mainwindow.cc index 52291b7..1295182 100644 --- a/examples/transcoder/mainwindow.cc +++ b/examples/transcoder/mainwindow.cc @@ -1,6 +1,10 @@ #include "mainwindow.hpp" +#include "audioencoderwidget.hpp" +#include "outputwidget.hpp" #include "previewwidget.hpp" #include "sourcewidget.hpp" +#include "stautuswidget.hpp" +#include "videoencoderwidget.hpp" #include #include @@ -12,8 +16,6 @@ #include -#define BUTTON_SIZE QSize(100, 35) - class MainWindow::MainWindowPrivate { public: @@ -27,108 +29,24 @@ class MainWindow::MainWindowPrivate sourceWidget = new SourceWidget(q_ptr); tabWidget = new QTabWidget(q_ptr); previewWidget = new PreviewWidget(q_ptr); + videoEncoderWidget = new VideoEncoderWidget(q_ptr); + videoEncoderWidget->setPreset(transcoder->presets(), transcoder->preset()); + videoEncoderWidget->setTune(transcoder->tunes(), transcoder->tune()); + videoEncoderWidget->setProfile(transcoder->profiles(), transcoder->profile()); + audioEncoderWidget = new AudioEncoderWidget(q_ptr); tabWidget->addTab(previewWidget, QCoreApplication::translate("MainWindowPrivate", "Preview")); + tabWidget->addTab(videoEncoderWidget, + QCoreApplication::translate("MainWindowPrivate", "Video")); + tabWidget->addTab(audioEncoderWidget, + QCoreApplication::translate("MainWindowPrivate", "Audio")); - subtitleTextEdit = new QTextEdit(q_ptr); - outTextEdit = new QTextEdit(q_ptr); - - audioCodecCbx = new QComboBox(q_ptr); - audioCodecCbx->setView(new QListView(audioCodecCbx)); - audioCodecCbx->setMaxVisibleItems(10); - audioCodecCbx->setStyleSheet("QComboBox {combobox-popup:0;}"); - auto audioCodecs = Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_AUDIO, true); - for (auto iter = audioCodecs.cbegin(); iter != audioCodecs.cend(); ++iter) { - audioCodecCbx->addItem(iter.value(), iter.key()); - } - audioCodecCbx->setCurrentIndex(audioCodecCbx->findData(AV_CODEC_ID_AAC)); - - videoCodecCbx = new QComboBox(q_ptr); - videoCodecCbx->setView(new QListView(videoCodecCbx)); - videoCodecCbx->setMaxVisibleItems(10); - videoCodecCbx->setStyleSheet("QComboBox {combobox-popup:0;}"); - auto videoCodecs = Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_VIDEO, true); - for (auto iter = videoCodecs.cbegin(); iter != videoCodecs.cend(); ++iter) { - videoCodecCbx->addItem(iter.value(), iter.key()); - } - videoCodecCbx->setCurrentIndex(videoCodecCbx->findData(AV_CODEC_ID_H264)); - - quailtySbx = new QSpinBox(q_ptr); - quailtySbx->setRange(2, 31); - quailtySbx->setToolTip(tr("smaller -> better")); - crfSbx = new QSpinBox(q_ptr); - crfSbx->setRange(0, 51); - crfSbx->setToolTip(tr("smaller -> better")); - crfSbx->setValue(18); - presetCbx = new QComboBox(q_ptr); - presetCbx->setView(new QListView(presetCbx)); - presetCbx->addItems(transcoder->presets()); - presetCbx->setCurrentText(transcoder->preset()); - tuneCbx = new QComboBox(q_ptr); - tuneCbx->setView(new QListView(tuneCbx)); - tuneCbx->addItems(transcoder->tunes()); - tuneCbx->setCurrentText(transcoder->tune()); - profileCbx = new QComboBox(q_ptr); - profileCbx->setView(new QListView(profileCbx)); - profileCbx->addItems(transcoder->profiles()); - profileCbx->setCurrentText(transcoder->profile()); - - widthLineEdit = new QLineEdit(q_ptr); - widthLineEdit->setValidator(new QIntValidator(0, INT_MAX, widthLineEdit)); - heightLineEdit = new QLineEdit(q_ptr); - heightLineEdit->setValidator(new QIntValidator(0, INT_MAX, heightLineEdit)); - keepAspectRatioCkb = new QCheckBox(tr("keepAspectRatio"), q_ptr); - keepAspectRatioCkb->setChecked(true); - - videoMinBitrateLineEdit = new QLineEdit(q_ptr); - videoMaxBitrateLineEdit = new QLineEdit(q_ptr); - - startButton = new QToolButton(q_ptr); - startButton->setText(QObject::tr("Start")); - startButton->setMinimumSize(BUTTON_SIZE); - progressBar = new QProgressBar(q_ptr); - progressBar->setRange(0, 100); - fpsLabel = new QLabel(q_ptr); - fpsLabel->setToolTip(QObject::tr("Video Encoder FPS.")); - fpsTimer = new QTimer(q_ptr); - } + outPutWidget = new OutPutWidget(q_ptr); + statusWidget = new StautusWidget(q_ptr); - [[nodiscard]] auto initVideoSetting() const -> QGroupBox * - { - auto *layout1 = new QHBoxLayout; - layout1->addWidget(new QLabel(tr("Width:"), q_ptr)); - layout1->addWidget(widthLineEdit); - layout1->addWidget(new QLabel(tr("height:"), q_ptr)); - layout1->addWidget(heightLineEdit); - layout1->addWidget(keepAspectRatioCkb); - auto *layout2 = new QHBoxLayout; - layout2->addWidget(new QLabel(tr("Min Bitrate:"), q_ptr)); - layout2->addWidget(videoMinBitrateLineEdit); - layout2->addWidget(new QLabel(tr("Max Bitrate:"), q_ptr)); - layout2->addWidget(videoMaxBitrateLineEdit); - - auto *groupBox = new QGroupBox(tr("Video"), q_ptr); - auto *layout = new QVBoxLayout(groupBox); - layout->addLayout(layout1); - layout->addLayout(layout2); - return groupBox; - } + fpsTimer = new QTimer(q_ptr); - [[nodiscard]] auto invalidSetting() const -> QGroupBox * - { - auto *groupBox = new QGroupBox(tr("Invalid setting"), q_ptr); - auto *layout = new QHBoxLayout(groupBox); - layout->addWidget(new QLabel(tr("Quality:"), q_ptr)); - layout->addWidget(quailtySbx); - layout->addWidget(new QLabel(tr("Crf:"), q_ptr)); - layout->addWidget(crfSbx); - layout->addWidget(new QLabel(tr("Preset:"), q_ptr)); - layout->addWidget(presetCbx); - layout->addWidget(new QLabel(tr("Tune:"), q_ptr)); - layout->addWidget(tuneCbx); - layout->addWidget(new QLabel(tr("Profile:"), q_ptr)); - layout->addWidget(profileCbx); - return groupBox; + subtitleTextEdit = new QTextEdit(q_ptr); } void initInputFileAttribute(const QString &filePath) const @@ -176,14 +94,6 @@ class MainWindow::MainWindowPrivate sourceWidget->setDuration(mediaInfo.duration / 1000); } - void calBitrate() const - { - auto w = widthLineEdit->text().toInt(); - auto h = heightLineEdit->text().toInt(); - videoMinBitrateLineEdit->setText(QString::number(w * h)); - videoMaxBitrateLineEdit->setText(QString::number(w * h * 4)); - } - MainWindow *q_ptr; Ffmpeg::Transcoder *transcoder; @@ -191,29 +101,14 @@ class MainWindow::MainWindowPrivate SourceWidget *sourceWidget; QTabWidget *tabWidget; PreviewWidget *previewWidget; + VideoEncoderWidget *videoEncoderWidget; + AudioEncoderWidget *audioEncoderWidget; + OutPutWidget *outPutWidget; + StautusWidget *statusWidget; - QTextEdit *subtitleTextEdit; - QTextEdit *outTextEdit; - - QComboBox *audioCodecCbx; - QComboBox *videoCodecCbx; - QSpinBox *quailtySbx; - QSpinBox *crfSbx; - QComboBox *presetCbx; - QComboBox *tuneCbx; - QComboBox *profileCbx; - - QLineEdit *widthLineEdit; - QLineEdit *heightLineEdit; - QSize originalSize = QSize(-1, -1); - QCheckBox *keepAspectRatioCkb; - QLineEdit *videoMinBitrateLineEdit; - QLineEdit *videoMaxBitrateLineEdit; - - QToolButton *startButton; - QProgressBar *progressBar; - QLabel *fpsLabel; QTimer *fpsTimer; + + QTextEdit *subtitleTextEdit; }; MainWindow::MainWindow(QWidget *parent) @@ -228,12 +123,6 @@ MainWindow::MainWindow(QWidget *parent) MainWindow::~MainWindow() = default; -void MainWindow::onVideoEncoderChanged() -{ - auto quantizer = Ffmpeg::getCodecQuantizer(d_ptr->videoCodecCbx->currentText()); - d_ptr->quailtySbx->setRange(quantizer.first, quantizer.second); -} - void MainWindow::onOpenInputFile() { const auto path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) @@ -248,8 +137,8 @@ void MainWindow::onOpenInputFile() } d_ptr->sourceWidget->setSource(filePath); - d_ptr->initInputFileAttribute(filePath); + d_ptr->outPutWidget->setOutputFileName(QFileInfo(filePath).fileName()); } void MainWindow::onResetConfig() @@ -286,49 +175,34 @@ void MainWindow::onOpenSubtitle() d_ptr->subtitleTextEdit->setPlainText(filePath); } -void MainWindow::onOpenOutputFile() -{ - const QString path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) - .value(0, QDir::homePath()); - const QString filePath - = QFileDialog::getSaveFileName(this, - tr("Save File"), - path, - tr("Audio Video (*.mp3 *.mp4 *.mkv *.rmvb)")); - if (filePath.isEmpty()) { - return; - } - - d_ptr->outTextEdit->setPlainText(filePath); -} - void MainWindow::onStart() { - if (d_ptr->startButton->text() == tr("Start")) { + if (d_ptr->statusWidget->status() == tr("Start")) { auto inPath = d_ptr->sourceWidget->source(); auto subtitlePath = d_ptr->subtitleTextEdit->toPlainText(); - auto outPath = d_ptr->outTextEdit->toPlainText(); + auto outPath = d_ptr->outPutWidget->outputFilePath(); if (inPath.isEmpty() || outPath.isEmpty()) { return; } d_ptr->transcoder->setInFilePath(inPath); d_ptr->transcoder->setOutFilePath(outPath); - d_ptr->transcoder->setAudioEncodecName(d_ptr->audioCodecCbx->currentText()); - d_ptr->transcoder->setVideoEncodecName(d_ptr->videoCodecCbx->currentText()); - d_ptr->transcoder->setSize( - {d_ptr->widthLineEdit->text().toInt(), d_ptr->heightLineEdit->text().toInt()}); + d_ptr->transcoder->setAudioEncodecName(d_ptr->audioEncoderWidget->encoder()); + d_ptr->transcoder->setVideoEncodecName(d_ptr->videoEncoderWidget->encoder()); + d_ptr->transcoder->setUseGpuDecode(d_ptr->videoEncoderWidget->gpuDecode()); + d_ptr->transcoder->setUseGpuEncode(d_ptr->videoEncoderWidget->gpuEncode()); + d_ptr->transcoder->setSize(d_ptr->videoEncoderWidget->videoSize()); if (QFile::exists(subtitlePath)) { d_ptr->transcoder->setSubtitleFilename(subtitlePath); } - d_ptr->transcoder->setQuailty(d_ptr->quailtySbx->value()); - d_ptr->transcoder->setMinBitrate(d_ptr->videoMinBitrateLineEdit->text().toInt()); - d_ptr->transcoder->setMaxBitrate(d_ptr->videoMaxBitrateLineEdit->text().toInt()); - d_ptr->transcoder->setCrf(d_ptr->crfSbx->value()); - d_ptr->transcoder->setPreset(d_ptr->presetCbx->currentText()); - d_ptr->transcoder->setProfile(d_ptr->profileCbx->currentText()); + d_ptr->transcoder->setQuailty(d_ptr->videoEncoderWidget->quality()); + d_ptr->transcoder->setMinBitrate(d_ptr->videoEncoderWidget->minBitrate()); + d_ptr->transcoder->setMaxBitrate(d_ptr->videoEncoderWidget->maxBitrate()); + d_ptr->transcoder->setCrf(d_ptr->videoEncoderWidget->crf()); + d_ptr->transcoder->setPreset(d_ptr->videoEncoderWidget->preset()); + d_ptr->transcoder->setProfile(d_ptr->videoEncoderWidget->profile()); d_ptr->transcoder->startTranscode(); - d_ptr->startButton->setText(tr("Stop")); + d_ptr->statusWidget->setStatus(tr("Stop")); auto filename = QFile::exists(inPath) ? QFileInfo(inPath).fileName() : QFileInfo(QUrl(inPath).toString()).fileName(); @@ -336,10 +210,10 @@ void MainWindow::onStart() d_ptr->fpsTimer->start(1000); - } else if (d_ptr->startButton->text() == tr("Stop")) { + } else if (d_ptr->statusWidget->status() == tr("Stop")) { d_ptr->transcoder->stopTranscode(); - d_ptr->progressBar->setValue(0); - d_ptr->startButton->setText(tr("Start")); + d_ptr->statusWidget->setProgress(0); + d_ptr->statusWidget->setStatus(tr("Start")); d_ptr->fpsTimer->stop(); } @@ -358,8 +232,9 @@ void MainWindow::onProcessEvents() auto eventPtr = d_ptr->transcoder->takePropertyChangeEvent(); switch (eventPtr->type()) { case Ffmpeg::PropertyChangeEvent::EventType::Position: { - // auto *positionEvent = dynamic_cast(eventPtr.data()); - // d_ptr->controlWidget->setPosition(positionEvent->position() / AV_TIME_BASE); + auto *positionEvent = dynamic_cast(eventPtr.data()); + d_ptr->statusWidget->setProgress(positionEvent->position() * 100.0 + / d_ptr->transcoder->duration()); } break; case Ffmpeg::PropertyChangeEvent::MediaTrack: { bool audioSet = false; @@ -370,28 +245,17 @@ void MainWindow::onProcessEvents() switch (track.mediaType) { case AVMEDIA_TYPE_AUDIO: if (!audioSet) { - auto index = d_ptr->audioCodecCbx->findData(track.codecId); - if (index > 0) { - d_ptr->audioCodecCbx->setCurrentIndex(index); + if (d_ptr->audioEncoderWidget->setEncoder(track.codecId)) { audioSet = true; } } break; case AVMEDIA_TYPE_VIDEO: if (!videoSet) { - auto index = d_ptr->videoCodecCbx->findData(track.codecId); - if (index > 0) { - d_ptr->videoCodecCbx->setCurrentIndex(index); + if (d_ptr->videoEncoderWidget->setEncoder(track.codecId)) { + d_ptr->videoEncoderWidget->setVideoSize(track.size); + videoSet = true; } - videoSet = true; - d_ptr->widthLineEdit->blockSignals(true); - d_ptr->heightLineEdit->blockSignals(true); - d_ptr->widthLineEdit->setText(QString::number(track.size.width())); - d_ptr->heightLineEdit->setText(QString::number(track.size.height())); - d_ptr->originalSize = track.size; - d_ptr->widthLineEdit->blockSignals(false); - d_ptr->heightLineEdit->blockSignals(false); - d_ptr->calBitrate(); } break; default: break; @@ -424,54 +288,24 @@ void MainWindow::setupUI() auto *subtitleBtn = new QToolButton(this); subtitleBtn->setText(tr("Add Subtitle")); - subtitleBtn->setMinimumSize(BUTTON_SIZE); connect(subtitleBtn, &QToolButton::clicked, this, &MainWindow::onOpenSubtitle); - auto *outBtn = new QToolButton(this); - outBtn->setText(tr("Open Out")); - outBtn->setMinimumSize(BUTTON_SIZE); - connect(outBtn, &QToolButton::clicked, this, &MainWindow::onOpenOutputFile); auto *editLayout = new QGridLayout; editLayout->addWidget(d_ptr->subtitleTextEdit, 1, 0, 1, 1); editLayout->addWidget(subtitleBtn, 1, 1, 1, 1); - editLayout->addWidget(d_ptr->outTextEdit, 2, 0, 1, 1); - editLayout->addWidget(outBtn, 2, 1, 1, 1); - - auto *groupLayout1 = new QHBoxLayout; - groupLayout1->addWidget(new QLabel(tr("Audio Codec Name:"), this)); - groupLayout1->addWidget(d_ptr->audioCodecCbx); - groupLayout1->addWidget(new QLabel(tr("Video Codec Name:"), this)); - groupLayout1->addWidget(d_ptr->videoCodecCbx); - auto *groupBox = new QGroupBox(tr("Encoder Settings"), this); - auto *groupLayout = new QVBoxLayout(groupBox); - groupLayout->addLayout(groupLayout1); - groupLayout->addWidget(d_ptr->invalidSetting()); - groupLayout->addWidget(d_ptr->initVideoSetting()); - - auto *useGpuCheckBox = new QCheckBox(tr("GPU Decode"), this); - useGpuCheckBox->setToolTip(tr("GPU Decode")); - useGpuCheckBox->setChecked(true); - connect(useGpuCheckBox, &QCheckBox::clicked, this, [this, useGpuCheckBox] { - d_ptr->transcoder->setUseGpuDecode(useGpuCheckBox->isChecked()); - }); - d_ptr->transcoder->setUseGpuDecode(useGpuCheckBox->isChecked()); - auto *displayLayout = new QHBoxLayout; - displayLayout->addWidget(d_ptr->startButton); - displayLayout->addWidget(useGpuCheckBox); - displayLayout->addWidget(d_ptr->progressBar); - displayLayout->addWidget(d_ptr->fpsLabel); auto *widget = new QWidget(this); auto *layout = new QVBoxLayout(widget); layout->addWidget(d_ptr->sourceWidget); layout->addWidget(d_ptr->tabWidget); + layout->addWidget(d_ptr->outPutWidget); setCentralWidget(widget); + statusBar()->addWidget(d_ptr->statusWidget); + auto *tempWidget = new QWidget(this); auto *tempLayout = new QVBoxLayout(tempWidget); tempLayout->addLayout(editLayout); - tempLayout->addWidget(groupBox); - tempLayout->addLayout(displayLayout); d_ptr->tabWidget->addTab(tempWidget, tr("Temp Widget")); } @@ -481,59 +315,16 @@ void MainWindow::buildConnect() &Ffmpeg::Transcoder::eventIncrease, this, &MainWindow::onProcessEvents); - - connect(d_ptr->sourceWidget, &SourceWidget::showMediaInfo, this, &MainWindow::onShowMediaInfo); - - connect(d_ptr->videoCodecCbx, - &QComboBox::currentTextChanged, - this, - &MainWindow::onVideoEncoderChanged); - - connect(d_ptr->widthLineEdit, &QLineEdit::textChanged, this, [this](const QString &text) { - if (!d_ptr->keepAspectRatioCkb->isChecked() || !d_ptr->originalSize.isValid()) { - return; - } - auto multiple = d_ptr->originalSize.width() * 1.0 / text.toInt(); - int height = d_ptr->originalSize.height() / multiple; - d_ptr->heightLineEdit->blockSignals(true); - d_ptr->heightLineEdit->setText(QString::number(height)); - d_ptr->heightLineEdit->blockSignals(false); - d_ptr->calBitrate(); - }); - connect(d_ptr->heightLineEdit, &QLineEdit::textChanged, this, [this](const QString &text) { - if (!d_ptr->keepAspectRatioCkb->isChecked() || !d_ptr->originalSize.isValid()) { - return; - } - auto multiple = d_ptr->originalSize.height() * 1.0 / text.toInt(); - int width = d_ptr->originalSize.width() / multiple; - d_ptr->widthLineEdit->blockSignals(true); - d_ptr->widthLineEdit->setText(QString::number(width)); - d_ptr->widthLineEdit->blockSignals(false); - d_ptr->calBitrate(); - }); - connect(d_ptr->keepAspectRatioCkb, &QCheckBox::stateChanged, this, [this] { - if (!d_ptr->keepAspectRatioCkb->isChecked() || !d_ptr->originalSize.isValid()) { - return; - } - auto multiple = d_ptr->originalSize.width() * 1.0 / d_ptr->widthLineEdit->text().toInt(); - int height = d_ptr->originalSize.height() / multiple; - d_ptr->heightLineEdit->blockSignals(true); - d_ptr->heightLineEdit->setText(QString::number(height)); - d_ptr->heightLineEdit->blockSignals(false); - d_ptr->calBitrate(); - }); - - connect(d_ptr->startButton, &QToolButton::clicked, this, &MainWindow::onStart); - connect(d_ptr->transcoder, &Ffmpeg::Transcoder::progressChanged, this, [this](qreal value) { - d_ptr->progressBar->setValue(value * 100); - }); connect(d_ptr->transcoder, &Ffmpeg::Transcoder::finished, this, [this] { - if (d_ptr->startButton->text() == tr("Stop")) { - d_ptr->startButton->click(); + if (d_ptr->statusWidget->status() == tr("Stop")) { + d_ptr->statusWidget->click(); } }); + + connect(d_ptr->sourceWidget, &SourceWidget::showMediaInfo, this, &MainWindow::onShowMediaInfo); + connect(d_ptr->statusWidget, &StautusWidget::start, this, &MainWindow::onStart); + connect(d_ptr->fpsTimer, &QTimer::timeout, this, [this] { - auto str = QString("FPS: %1").arg(QString::number(d_ptr->transcoder->fps(), 'f', 2)); - d_ptr->fpsLabel->setText(str); + d_ptr->statusWidget->setFPS(d_ptr->transcoder->fps()); }); } diff --git a/examples/transcoder/mainwindow.hpp b/examples/transcoder/mainwindow.hpp index cefb2d9..d1b5d1f 100644 --- a/examples/transcoder/mainwindow.hpp +++ b/examples/transcoder/mainwindow.hpp @@ -15,11 +15,9 @@ class MainWindow : public QMainWindow ~MainWindow() override; private slots: - void onVideoEncoderChanged(); void onOpenInputFile(); void onResetConfig(); void onOpenSubtitle(); - void onOpenOutputFile(); void onStart(); void onShowMediaInfo(); diff --git a/examples/transcoder/outputwidget.cc b/examples/transcoder/outputwidget.cc new file mode 100644 index 0000000..59dfd3e --- /dev/null +++ b/examples/transcoder/outputwidget.cc @@ -0,0 +1,64 @@ +#include "outputwidget.hpp" + +#include + +class OutPutWidget::OutPutWidgetPrivate +{ +public: + explicit OutPutWidgetPrivate(OutPutWidget *q) + : q_ptr(q) + { + outLineEdit = new QLineEdit(q_ptr); + } + + OutPutWidget *q_ptr; + + QLineEdit *outLineEdit; +}; + +OutPutWidget::OutPutWidget(QWidget *parent) + : QWidget{parent} + , d_ptr(new OutPutWidgetPrivate(this)) +{ + setupUI(); + buildConnect(); +} + +OutPutWidget::~OutPutWidget() {} + +void OutPutWidget::setOutputFileName(const QString &fileName) +{ + const auto path = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation) + .value(0, QDir::homePath()); + d_ptr->outLineEdit->setText(QDir(path).filePath(fileName)); +} + +auto OutPutWidget::outputFilePath() const -> QString +{ + return d_ptr->outLineEdit->text().trimmed(); +} + +void OutPutWidget::onBrowse() +{ + const auto filePath = QFileDialog::getSaveFileName(this, + tr("Save File"), + d_ptr->outLineEdit->text().trimmed()); + if (filePath.isEmpty()) { + return; + } + d_ptr->outLineEdit->setText(filePath); +} + +void OutPutWidget::setupUI() +{ + auto *button = new QToolButton(this); + button->setText(tr("Browse")); + connect(button, &QToolButton::clicked, this, &OutPutWidget::onBrowse); + + auto *layout = new QHBoxLayout(this); + layout->addWidget(new QLabel(tr("Save File:"), this)); + layout->addWidget(d_ptr->outLineEdit); + layout->addWidget(button); +} + +void OutPutWidget::buildConnect() {} diff --git a/examples/transcoder/outputwidget.hpp b/examples/transcoder/outputwidget.hpp new file mode 100644 index 0000000..cd86133 --- /dev/null +++ b/examples/transcoder/outputwidget.hpp @@ -0,0 +1,27 @@ +#ifndef OUTPUTWIDGET_HPP +#define OUTPUTWIDGET_HPP + +#include + +class OutPutWidget : public QWidget +{ + Q_OBJECT +public: + explicit OutPutWidget(QWidget *parent = nullptr); + ~OutPutWidget() override; + + void setOutputFileName(const QString &fileName); + auto outputFilePath() const -> QString; + +private slots: + void onBrowse(); + +private: + void setupUI(); + void buildConnect(); + + class OutPutWidgetPrivate; + QScopedPointer d_ptr; +}; + +#endif // OUTPUTWIDGET_HPP diff --git a/examples/transcoder/previewwidget.cc b/examples/transcoder/previewwidget.cc index beb674d..f8c5d75 100644 --- a/examples/transcoder/previewwidget.cc +++ b/examples/transcoder/previewwidget.cc @@ -17,6 +17,7 @@ class PreviewWidget::PreviewWidgetPrivate renderPtr->widget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); renderPtr->setBackgroundColor(Qt::white); infoLabel = new QLabel(q_ptr); + infoLabel->setAlignment(Qt::AlignCenter); leftButton = new QToolButton(q_ptr); leftButton->setMinimumSize(buttonSize); leftButton->setText("<"); diff --git a/examples/transcoder/sourcewidget.cc b/examples/transcoder/sourcewidget.cc index a943991..a4368b0 100644 --- a/examples/transcoder/sourcewidget.cc +++ b/examples/transcoder/sourcewidget.cc @@ -8,7 +8,7 @@ class SourceWidget::SourceWidgetPrivate explicit SourceWidgetPrivate(SourceWidget *q) : q_ptr(q) { - inTextEdit = new QTextEdit(q_ptr); + inLineEdit = new QLineEdit(q_ptr); infoLabel = new QLabel(q_ptr); infoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); startTimeEdit = new QTimeEdit(q_ptr); @@ -20,7 +20,7 @@ class SourceWidget::SourceWidgetPrivate SourceWidget *q_ptr; - QTextEdit *inTextEdit; + QLineEdit *inLineEdit; QLabel *infoLabel; QTimeEdit *startTimeEdit; QTimeEdit *endTimeEdit; @@ -35,16 +35,16 @@ SourceWidget::SourceWidget(QWidget *parent) buildConnect(); } -SourceWidget::~SourceWidget() {} +SourceWidget::~SourceWidget() = default; void SourceWidget::setSource(const QString &source) { - d_ptr->inTextEdit->setPlainText(source); + d_ptr->inLineEdit->setText(source); } auto SourceWidget::source() const -> QString { - return d_ptr->inTextEdit->toPlainText(); + return d_ptr->inLineEdit->text().trimmed(); } void SourceWidget::setDuration(qint64 duration) @@ -81,7 +81,7 @@ void SourceWidget::setupUI() auto *layout1 = new QHBoxLayout; layout1->addWidget(new QLabel(tr("Input File:"), this)); - layout1->addWidget(d_ptr->inTextEdit); + layout1->addWidget(d_ptr->inLineEdit); auto *layout2 = new QHBoxLayout; layout2->addWidget(fileInfoButton); diff --git a/examples/transcoder/stautuswidget.cc b/examples/transcoder/stautuswidget.cc new file mode 100644 index 0000000..3a9fbfb --- /dev/null +++ b/examples/transcoder/stautuswidget.cc @@ -0,0 +1,75 @@ +#include "stautuswidget.hpp" + +#include + +class StautusWidget::StautusWidgetPrivate +{ +public: + explicit StautusWidgetPrivate(StautusWidget *q) + : q_ptr(q) + { + startButton = new QToolButton(q_ptr); + startButton->setText(QCoreApplication::translate("StautusWidgetPrivate", "Start")); + progressBar = new QProgressBar(q_ptr); + progressBar->setRange(0, 100); + fpsLabel = new QLabel(q_ptr); + fpsLabel->setToolTip( + QCoreApplication::translate("StautusWidgetPrivate", "Video Encoder FPS.")); + } + + StautusWidget *q_ptr = nullptr; + + QToolButton *startButton; + QProgressBar *progressBar; + QLabel *fpsLabel; +}; + +StautusWidget::StautusWidget(QWidget *parent) + : QWidget{parent} + , d_ptr(new StautusWidgetPrivate(this)) +{ + setupUI(); + buildConnect(); +} + +StautusWidget::~StautusWidget() {} + +void StautusWidget::setStatus(const QString &status) +{ + d_ptr->startButton->setText(status); +} + +QString StautusWidget::status() const +{ + return d_ptr->startButton->text(); +} + +void StautusWidget::setProgress(int progress) +{ + d_ptr->progressBar->setValue(progress); +} + +void StautusWidget::setFPS(double fps) +{ + auto text = QString("FPS: %1").arg(QString::number(fps, 'f', 2)); + d_ptr->fpsLabel->setText(text); + d_ptr->fpsLabel->setToolTip(text); +} + +void StautusWidget::click() +{ + d_ptr->startButton->click(); +} + +void StautusWidget::setupUI() +{ + auto *layout = new QHBoxLayout(this); + layout->addWidget(d_ptr->startButton); + layout->addWidget(d_ptr->progressBar); + layout->addWidget(d_ptr->fpsLabel); +} + +void StautusWidget::buildConnect() +{ + connect(d_ptr->startButton, &QToolButton::clicked, this, &StautusWidget::start); +} diff --git a/examples/transcoder/stautuswidget.hpp b/examples/transcoder/stautuswidget.hpp new file mode 100644 index 0000000..3288531 --- /dev/null +++ b/examples/transcoder/stautuswidget.hpp @@ -0,0 +1,33 @@ +#ifndef STAUTUSWIDGET_HPP +#define STAUTUSWIDGET_HPP + +#include + +class StautusWidget : public QWidget +{ + Q_OBJECT +public: + explicit StautusWidget(QWidget *parent = nullptr); + ~StautusWidget() override; + + void setStatus(const QString &status); + QString status() const; + + void setProgress(int progress); + + void setFPS(double fps); + + void click(); + +signals: + void start(); + +private: + void setupUI(); + void buildConnect(); + + class StautusWidgetPrivate; + QScopedPointer d_ptr; +}; + +#endif // STAUTUSWIDGET_HPP diff --git a/examples/transcoder/transcoder.pro b/examples/transcoder/transcoder.pro index 38ee124..7956cba 100644 --- a/examples/transcoder/transcoder.pro +++ b/examples/transcoder/transcoder.pro @@ -4,7 +4,7 @@ QT += core gui widgets network multimedia openglwidgets core5compat TEMPLATE = app -TARGET = QFfmpegTranscoder +TARGET = Transcoder LIBS += \ -L$$APP_OUTPUT_PATH/../libs \ @@ -15,15 +15,23 @@ LIBS += \ include(../../3rdparty/3rdparty.pri) SOURCES += \ + audioencoderwidget.cc \ main.cc \ mainwindow.cc \ + outputwidget.cc \ previewwidget.cc \ - sourcewidget.cc + sourcewidget.cc \ + stautuswidget.cc \ + videoencoderwidget.cc HEADERS += \ + audioencoderwidget.hpp \ mainwindow.hpp \ + outputwidget.hpp \ previewwidget.hpp \ - sourcewidget.hpp + sourcewidget.hpp \ + stautuswidget.hpp \ + videoencoderwidget.hpp DESTDIR = $$APP_OUTPUT_PATH diff --git a/examples/transcoder/videoencoderwidget.cc b/examples/transcoder/videoencoderwidget.cc new file mode 100644 index 0000000..fb46794 --- /dev/null +++ b/examples/transcoder/videoencoderwidget.cc @@ -0,0 +1,293 @@ +#include "videoencoderwidget.hpp" + +#include + +#include + +class VideoEncoderWidget::VideoEncoderWidgetPrivate +{ +public: + explicit VideoEncoderWidgetPrivate(VideoEncoderWidget *q) + : q_ptr(q) + { + const auto *const comboBoxStyleSheet{"QComboBox {combobox-popup:0;}"}; + videoEncoderCbx = new QComboBox(q_ptr); + videoEncoderCbx->setView(new QListView(videoEncoderCbx)); + videoEncoderCbx->setMaxVisibleItems(10); + videoEncoderCbx->setStyleSheet(comboBoxStyleSheet); + auto videoCodecs = Ffmpeg::getCurrentSupportCodecs(AVMEDIA_TYPE_VIDEO, true); + for (auto iter = videoCodecs.cbegin(); iter != videoCodecs.cend(); ++iter) { + videoEncoderCbx->addItem(iter.value(), iter.key()); + } + videoEncoderCbx->setCurrentIndex(videoEncoderCbx->findData(AV_CODEC_ID_H264)); + videoEncoderCbx->model()->sort(0); + + gpuEncodeCbx = new QCheckBox(QCoreApplication::translate("VideoEncoderWidgetPrivate", + "Use GPU Encode"), + q_ptr); + gpuEncodeCbx->setChecked(true); + gpuDecodeCbx = new QCheckBox(QCoreApplication::translate("VideoEncoderWidgetPrivate", + "Use GPU Decode"), + q_ptr); + gpuDecodeCbx->setChecked(true); + + quailtySbx = new QSpinBox(q_ptr); + quailtySbx->setRange(2, 31); + quailtySbx->setToolTip( + QCoreApplication::translate("VideoEncoderWidgetPrivate", "smaller -> better")); + crfSbx = new QSpinBox(q_ptr); + crfSbx->setRange(0, 51); + crfSbx->setToolTip( + QCoreApplication::translate("VideoEncoderWidgetPrivate", "smaller -> better")); + crfSbx->setValue(18); + presetCbx = new QComboBox(q_ptr); + presetCbx->setView(new QListView(presetCbx)); + tuneCbx = new QComboBox(q_ptr); + tuneCbx->setView(new QListView(tuneCbx)); + profileCbx = new QComboBox(q_ptr); + profileCbx->setView(new QListView(profileCbx)); + + widthSbx = new QSpinBox(q_ptr); + widthSbx->setRange(0, INT_MAX); + heightSbx = new QSpinBox(q_ptr); + heightSbx->setRange(0, INT_MAX); + aspectCheckBox = new QCheckBox(QCoreApplication::translate("VideoEncoderWidgetPrivate", + "Keep aspect ratio:"), + q_ptr); + aspectCheckBox->setChecked(true); + + minBitrateSbx = new QSpinBox(q_ptr); + minBitrateSbx->setRange(0, INT_MAX); + maxBitrateSbx = new QSpinBox(q_ptr); + maxBitrateSbx->setRange(0, INT_MAX); + } + + void calBitrate() const + { + auto w = widthSbx->value(); + auto h = heightSbx->value(); + minBitrateSbx->setValue(w * h); + maxBitrateSbx->setValue(w * h * 4); + } + + VideoEncoderWidget *q_ptr; + + QComboBox *videoEncoderCbx; + QCheckBox *gpuEncodeCbx; + QCheckBox *gpuDecodeCbx; + + QSpinBox *quailtySbx; + QSpinBox *crfSbx; + QComboBox *presetCbx; + QComboBox *tuneCbx; + QComboBox *profileCbx; + + QSpinBox *widthSbx; + QSpinBox *heightSbx; + QCheckBox *aspectCheckBox; + + QSpinBox *minBitrateSbx; + QSpinBox *maxBitrateSbx; + + QSize originalSize; +}; + +VideoEncoderWidget::VideoEncoderWidget(QWidget *parent) + : QWidget{parent} + , d_ptr(new VideoEncoderWidgetPrivate(this)) +{ + setupUI(); + buildConnect(); +} + +VideoEncoderWidget::~VideoEncoderWidget() = default; + +auto VideoEncoderWidget::setEncoder(AVCodecID codecId) -> bool +{ + auto index = d_ptr->videoEncoderCbx->findData(codecId); + auto finded = (index >= 0); + if (finded) { + d_ptr->videoEncoderCbx->setCurrentIndex(index); + } + return finded; +} + +auto VideoEncoderWidget::encoder() const -> QString +{ + return d_ptr->videoEncoderCbx->currentText(); +} + +void VideoEncoderWidget::setPreset(const QStringList &presets, const QString ¤t) +{ + d_ptr->presetCbx->clear(); + d_ptr->presetCbx->addItems(presets); + d_ptr->presetCbx->setCurrentText(current); +} + +auto VideoEncoderWidget::preset() const -> QString +{ + return d_ptr->presetCbx->currentText(); +} + +void VideoEncoderWidget::setTune(const QStringList &tunes, const QString ¤t) +{ + d_ptr->tuneCbx->clear(); + d_ptr->tuneCbx->addItems(tunes); + d_ptr->tuneCbx->setCurrentText(current); +} + +auto VideoEncoderWidget::tune() const -> QString +{ + return d_ptr->tuneCbx->currentText(); +} + +void VideoEncoderWidget::setProfile(const QStringList &profiles, const QString ¤t) +{ + d_ptr->profileCbx->clear(); + d_ptr->profileCbx->addItems(profiles); + d_ptr->profileCbx->setCurrentText(current); +} + +auto VideoEncoderWidget::profile() const -> QString +{ + return d_ptr->profileCbx->currentText(); +} + +void VideoEncoderWidget::setVideoSize(const QSize &size) +{ + d_ptr->widthSbx->blockSignals(true); + d_ptr->heightSbx->blockSignals(true); + d_ptr->widthSbx->setValue(size.width()); + d_ptr->heightSbx->setValue(size.height()); + d_ptr->widthSbx->blockSignals(false); + d_ptr->heightSbx->blockSignals(false); + + d_ptr->originalSize = size; + d_ptr->calBitrate(); +} + +auto VideoEncoderWidget::videoSize() const -> QSize +{ + return {d_ptr->widthSbx->value(), d_ptr->heightSbx->value()}; +} + +auto VideoEncoderWidget::quality() const -> int +{ + return d_ptr->quailtySbx->value(); +} + +auto VideoEncoderWidget::minBitrate() const -> int +{ + return d_ptr->minBitrateSbx->value(); +} + +auto VideoEncoderWidget::gpuEncode() const -> bool +{ + return d_ptr->gpuEncodeCbx->isChecked(); +} + +auto VideoEncoderWidget::gpuDecode() const -> bool +{ + return d_ptr->gpuDecodeCbx->isChecked(); +} + +auto VideoEncoderWidget::maxBitrate() const -> int +{ + return d_ptr->maxBitrateSbx->value(); +} + +auto VideoEncoderWidget::crf() const -> int +{ + return d_ptr->crfSbx->value(); +} + +void VideoEncoderWidget::onEncoderChanged() +{ + auto quantizer = Ffmpeg::getCodecQuantizer(d_ptr->videoEncoderCbx->currentText()); + if (quantizer.first < 0 || quantizer.second < 0) { + return; + } + d_ptr->quailtySbx->setRange(quantizer.first, quantizer.second); +} + +void VideoEncoderWidget::onVideoWidthChanged() +{ + if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->originalSize.isValid()) { + return; + } + auto multiple = d_ptr->originalSize.width() * 1.0 / d_ptr->widthSbx->value(); + int height = d_ptr->originalSize.height() / multiple; + d_ptr->heightSbx->blockSignals(true); + d_ptr->heightSbx->setValue(height); + d_ptr->heightSbx->blockSignals(false); + d_ptr->calBitrate(); +} + +void VideoEncoderWidget::onVideoHeightChanged() +{ + if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->originalSize.isValid()) { + return; + } + auto multiple = d_ptr->originalSize.height() * 1.0 / d_ptr->heightSbx->value(); + int width = d_ptr->originalSize.width() / multiple; + d_ptr->widthSbx->blockSignals(true); + d_ptr->widthSbx->setValue(width); + d_ptr->widthSbx->blockSignals(false); + d_ptr->calBitrate(); +} + +void VideoEncoderWidget::setupUI() +{ + auto *codecLayout = new QHBoxLayout; + codecLayout->setSpacing(20); + codecLayout->addWidget(new QLabel(tr("Encoder:"), this)); + codecLayout->addWidget(d_ptr->videoEncoderCbx); + codecLayout->addStretch(); + codecLayout->addWidget(d_ptr->gpuEncodeCbx); + codecLayout->addWidget(d_ptr->gpuDecodeCbx); + + auto *invailedGroupBox = new QGroupBox(tr("Invalid setting"), this); + auto *invailedLayout = new QFormLayout(invailedGroupBox); + invailedLayout->addRow(tr("Quality:"), d_ptr->quailtySbx); + invailedLayout->addRow(tr("Crf:"), d_ptr->crfSbx); + invailedLayout->addRow(tr("Preset:"), d_ptr->presetCbx); + invailedLayout->addRow(tr("Tune:"), d_ptr->tuneCbx); + invailedLayout->addRow(tr("Profile:"), d_ptr->profileCbx); + + auto *sizeGroupBox = new QGroupBox(tr("Size"), this); + auto *sizeLayout = new QFormLayout(sizeGroupBox); + sizeLayout->addRow(tr("Width:"), d_ptr->widthSbx); + sizeLayout->addRow(tr("Height:"), d_ptr->heightSbx); + sizeLayout->addRow(d_ptr->aspectCheckBox); + + auto *bitrateGroupBox = new QGroupBox(tr("Bitrate"), this); + auto *bitrateLayout = new QFormLayout(bitrateGroupBox); + bitrateLayout->addRow(tr("Min Bitrate:"), d_ptr->minBitrateSbx); + bitrateLayout->addRow(tr("Max Bitrate:"), d_ptr->maxBitrateSbx); + + auto *layout = new QGridLayout(this); + layout->addLayout(codecLayout, 0, 0, 1, 2); + layout->addWidget(invailedGroupBox, 1, 0, 2, 1); + layout->addWidget(sizeGroupBox, 1, 1, 1, 1); + layout->addWidget(bitrateGroupBox, 2, 1, 1, 1); +} + +void VideoEncoderWidget::buildConnect() +{ + connect(d_ptr->videoEncoderCbx, + &QComboBox::currentTextChanged, + this, + &VideoEncoderWidget::onEncoderChanged); + connect(d_ptr->widthSbx, + &QSpinBox::valueChanged, + this, + &VideoEncoderWidget::onVideoWidthChanged); + connect(d_ptr->heightSbx, + &QSpinBox::valueChanged, + this, + &VideoEncoderWidget::onVideoHeightChanged); + connect(d_ptr->aspectCheckBox, + &QCheckBox::stateChanged, + this, + &VideoEncoderWidget::onVideoWidthChanged); +} diff --git a/examples/transcoder/videoencoderwidget.hpp b/examples/transcoder/videoencoderwidget.hpp new file mode 100644 index 0000000..9ad128b --- /dev/null +++ b/examples/transcoder/videoencoderwidget.hpp @@ -0,0 +1,54 @@ +#ifndef VIDEOENCODERWIDGET_HPP +#define VIDEOENCODERWIDGET_HPP + +#include + +extern "C" { +#include +} + +class VideoEncoderWidget : public QWidget +{ + Q_OBJECT +public: + explicit VideoEncoderWidget(QWidget *parent = nullptr); + ~VideoEncoderWidget() override; + + auto setEncoder(AVCodecID codecId) -> bool; + [[nodiscard]] auto encoder() const -> QString; + + void setPreset(const QStringList &presets, const QString ¤t); + [[nodiscard]] auto preset() const -> QString; + + void setTune(const QStringList &tunes, const QString ¤t); + [[nodiscard]] auto tune() const -> QString; + + void setProfile(const QStringList &profiles, const QString ¤t); + [[nodiscard]] auto profile() const -> QString; + + void setVideoSize(const QSize &size); + [[nodiscard]] auto videoSize() const -> QSize; + + [[nodiscard]] auto quality() const -> int; + [[nodiscard]] auto crf() const -> int; + + [[nodiscard]] auto minBitrate() const -> int; + [[nodiscard]] auto maxBitrate() const -> int; + + [[nodiscard]] auto gpuEncode() const -> bool; + [[nodiscard]] auto gpuDecode() const -> bool; + +private slots: + void onEncoderChanged(); + void onVideoWidthChanged(); + void onVideoHeightChanged(); + +private: + void setupUI(); + void buildConnect(); + + class VideoEncoderWidgetPrivate; + QScopedPointer d_ptr; +}; + +#endif // VIDEOENCODERWIDGET_HPP diff --git a/ffmpeg/codeccontext.cpp b/ffmpeg/codeccontext.cpp index 8dfc963..f741515 100644 --- a/ffmpeg/codeccontext.cpp +++ b/ffmpeg/codeccontext.cpp @@ -72,6 +72,7 @@ CodecContext::CodecContext(const AVCodec *codec, QObject *parent) : QObject(parent) , d_ptr(new CodecContextPrivate(this)) { + qInfo() << "AVCodec:" << codec->long_name; d_ptr->codecCtx = avcodec_alloc_context3(codec); d_ptr->init(); Q_ASSERT(d_ptr->codecCtx != nullptr); @@ -263,7 +264,7 @@ void CodecContext::setCrf(int crf) { // 设置crf参数,范围是0-51,0是无损,23是默认值,51是最差质量 Q_ASSERT(crf >= 0 && crf <= 51); - av_opt_set(d_ptr->codecCtx->priv_data, "crf", QString::number(crf).toLocal8Bit().constData(), 0); + av_opt_set_double(d_ptr->codecCtx->priv_data, "crf", crf, 0); } void CodecContext::setPreset(const QString &preset) @@ -291,6 +292,7 @@ auto CodecContext::open() -> bool auto CodecContext::sendPacket(Packet *packet) -> bool { int ret = avcodec_send_packet(d_ptr->codecCtx, packet->avPacket()); + AVERROR(EINVAL); ERROR_RETURN(ret) } diff --git a/ffmpeg/ffmpegutils.cc b/ffmpeg/ffmpegutils.cc index fc3fc01..ccc43a0 100644 --- a/ffmpeg/ffmpegutils.cc +++ b/ffmpeg/ffmpegutils.cc @@ -151,10 +151,10 @@ auto compareAVRational(const AVRational &a, const AVRational &b) -> bool return a.den == b.den && a.num == b.num; } -auto getCodecQuantizer(const QString &codecname) -> QPair +auto getCodecQuantizer(const QString &codecName) -> QPair { QScopedPointer contextInfoPtr(new AVContextInfo); - if (!contextInfoPtr->initEncoder(codecname)) { + if (!contextInfoPtr->initEncoder(codecName)) { return {-1, -1}; } auto quantizer = contextInfoPtr->codecCtx()->quantizer(); diff --git a/ffmpeg/transcoder.cc b/ffmpeg/transcoder.cc index 622b08e..002a7ec 100644 --- a/ffmpeg/transcoder.cc +++ b/ffmpeg/transcoder.cc @@ -164,7 +164,8 @@ class Transcoder::TranscoderPrivate != 0) { avCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } - contextInfoPtr->openCodec(AVContextInfo::GpuEncode); + contextInfoPtr->openCodec(useGpuEncode ? AVContextInfo::GpuEncode + : AVContextInfo::NotUseGpu); auto ret = avcodec_parameters_from_context(stream->codecpar, contextInfoPtr->codecCtx()->avCodecCtx()); if (ret < 0) { @@ -388,7 +389,7 @@ class Transcoder::TranscoderPrivate void loop() { auto duration = inFormatContext->duration(); - while (runing) { + while (runing.load()) { PacketPtr packetPtr(new Packet); if (!inFormatContext->readFrame(packetPtr.get())) { break; @@ -413,7 +414,7 @@ class Transcoder::TranscoderPrivate calculatePts(packetPtr.data(), transcodeContexts.at(stream_index)->decContextInfoPtr.data()); - emit q_ptr->progressChanged(packetPtr->pts() / duration); + addPropertyChangeEvent(new PositionEvent(packetPtr->pts())); if (transcodeCtx->decContextInfoPtr->mediaType() == AVMEDIA_TYPE_VIDEO) { fpsPtr->update(); } @@ -512,6 +513,7 @@ class Transcoder::TranscoderPrivate QString profile = "main"; bool useGpuDecode = false; + bool useGpuEncode = false; std::atomic_bool runing = true; QScopedPointer fpsPtr; @@ -536,9 +538,14 @@ Transcoder::~Transcoder() stopTranscode(); } -void Transcoder::setUseGpuDecode(bool useGpu) +void Transcoder::setUseGpuDecode(bool use) +{ + d_ptr->useGpuDecode = use; +} + +void Transcoder::setUseGpuEncode(bool use) { - d_ptr->useGpuDecode = useGpu; + d_ptr->useGpuEncode = use; } void Transcoder::setInFilePath(const QString &filePath) diff --git a/ffmpeg/transcoder.hpp b/ffmpeg/transcoder.hpp index 5e57f8d..556529b 100644 --- a/ffmpeg/transcoder.hpp +++ b/ffmpeg/transcoder.hpp @@ -22,7 +22,8 @@ class FFMPEG_EXPORT Transcoder : public QThread explicit Transcoder(QObject *parent = nullptr); ~Transcoder() override; - void setUseGpuDecode(bool useGpu); + void setUseGpuDecode(bool use); + void setUseGpuEncode(bool use); void setInFilePath(const QString &filePath); auto parseInputFile() -> bool; @@ -71,7 +72,6 @@ class FFMPEG_EXPORT Transcoder : public QThread auto takePropertyChangeEvent() -> PropertyChangeEventPtr; signals: - void progressChanged(qreal); // 0.XXXX void eventIncrease(); protected: