From ea15fb6810882923f91de19aded0fa015461cfca 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, 14 Mar 2024 18:43:42 +0800 Subject: [PATCH] =?UTF-8?q?transcode=EF=BC=9A=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E8=BD=AC=E7=A0=81stream=EF=BC=8C=E6=8E=92?= =?UTF-8?q?=E5=87=BA=E5=85=B6=E4=BD=99stream=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/transcoder/CMakeLists.txt | 2 + examples/transcoder/commonwidgets.cc | 17 +++ examples/transcoder/commonwidgets.hpp | 13 +++ examples/transcoder/mainwindow.cc | 20 +++- examples/transcoder/styleditemdelegate.cc | 27 ++--- examples/transcoder/transcoder.pro | 2 + examples/transcoder/videoencoderwidget.cc | 58 +++++----- examples/transcoder/videoencoderwidget.hpp | 2 + ffmpeg/codeccontext.cpp | 9 +- ffmpeg/encodecontext.cc | 2 +- ffmpeg/encodecontext.hpp | 3 +- ffmpeg/ffmpegutils.cc | 7 ++ ffmpeg/ffmpegutils.hpp | 2 + ffmpeg/filter/filter.cc | 4 +- ffmpeg/formatcontext.cpp | 22 ++-- ffmpeg/transcoder.cc | 123 +++++++++++++-------- ffmpeg/transcoder.hpp | 15 +-- ffmpeg/transcodercontext.hpp | 3 + 18 files changed, 204 insertions(+), 127 deletions(-) create mode 100644 examples/transcoder/commonwidgets.cc create mode 100644 examples/transcoder/commonwidgets.hpp diff --git a/examples/transcoder/CMakeLists.txt b/examples/transcoder/CMakeLists.txt index 81ae559..ab58140 100644 --- a/examples/transcoder/CMakeLists.txt +++ b/examples/transcoder/CMakeLists.txt @@ -5,6 +5,8 @@ set(PROJECT_SOURCES audioencodertableview.hpp audioencoderwidget.cc audioencoderwidget.hpp + commonwidgets.cc + commonwidgets.hpp main.cc mainwindow.cc mainwindow.hpp diff --git a/examples/transcoder/commonwidgets.cc b/examples/transcoder/commonwidgets.cc new file mode 100644 index 0000000..0e159be --- /dev/null +++ b/examples/transcoder/commonwidgets.cc @@ -0,0 +1,17 @@ +#include "commonwidgets.hpp" + +#include + +namespace CommonWidgets { + +auto createComboBox(QWidget *parent) -> QComboBox * +{ + const auto *comboBoxStyleSheet = "QComboBox {combobox-popup:0;}"; + auto *comboBox = new QComboBox(parent); + comboBox->setView(new QListView(comboBox)); + comboBox->setMaxVisibleItems(10); + comboBox->setStyleSheet(comboBoxStyleSheet); + return comboBox; +} + +} // namespace CommonWidgets diff --git a/examples/transcoder/commonwidgets.hpp b/examples/transcoder/commonwidgets.hpp new file mode 100644 index 0000000..d3ab6be --- /dev/null +++ b/examples/transcoder/commonwidgets.hpp @@ -0,0 +1,13 @@ +#ifndef COMMONWIDGETS_HPP +#define COMMONWIDGETS_HPP + +class QComboBox; +class QWidget; + +namespace CommonWidgets { + +auto createComboBox(QWidget *parent) -> QComboBox *; + +} // namespace CommonWidgets + +#endif // COMMONWIDGETS_HPP diff --git a/examples/transcoder/mainwindow.cc b/examples/transcoder/mainwindow.cc index a9c1d64..cbbe3f2 100644 --- a/examples/transcoder/mainwindow.cc +++ b/examples/transcoder/mainwindow.cc @@ -60,7 +60,7 @@ class MainWindow::MainWindowPrivate transcoder->parseInputFile(); } - void initUI() + void initUI() const { QSize size; double frameRate = 0; @@ -198,9 +198,20 @@ void MainWindow::onStart() d_ptr->transcoder->setInFilePath(inPath); d_ptr->transcoder->setOutFilePath(outPath); - d_ptr->transcoder->setVideoEncodeContext(d_ptr->videoEncoderWidget->encodeContext()); - d_ptr->transcoder->setAudioEncodeContext( - d_ptr->audioEncoderWidget->encodeContexts().first()); + d_ptr->transcoder->setGpuDecode(d_ptr->videoEncoderWidget->isGpuDecode()); + + Ffmpeg::EncodeContexts encodeContexts(d_ptr->transcoder->decodeContexts().size()); + auto videoEncodeContexts = d_ptr->videoEncoderWidget->encodeContext(); + auto audioEncodeContexts = d_ptr->audioEncoderWidget->encodeContexts(); + auto subtitleEncodeContexts = d_ptr->subtitleEncoderWidget->encodeContexts(); + encodeContexts.replace(videoEncodeContexts.streamIndex, videoEncodeContexts); + for (const auto &context : std::as_const(audioEncodeContexts)) { + encodeContexts.replace(context.streamIndex, context); + } + for (const auto &context : std::as_const(subtitleEncodeContexts)) { + encodeContexts.replace(context.streamIndex, context); + } + d_ptr->transcoder->setEncodeContexts(encodeContexts); if (QFile::exists(subtitlePath)) { d_ptr->transcoder->setSubtitleFilename(subtitlePath); @@ -213,7 +224,6 @@ void MainWindow::onStart() setWindowTitle(filename); d_ptr->fpsTimer->start(1000); - } else if (d_ptr->statusWidget->status() == tr("Stop")) { d_ptr->transcoder->stopTranscode(); d_ptr->statusWidget->setProgress(0); diff --git a/examples/transcoder/styleditemdelegate.cc b/examples/transcoder/styleditemdelegate.cc index ab84a03..cb4a471 100644 --- a/examples/transcoder/styleditemdelegate.cc +++ b/examples/transcoder/styleditemdelegate.cc @@ -1,20 +1,11 @@ #include "styleditemdelegate.hpp" +#include "commonwidgets.hpp" #include #include #include -auto createComboBox(QWidget *parent) -> QComboBox * -{ - const auto *comboBoxStyleSheet = "QComboBox {combobox-popup:0;}"; - auto *comboBox = new QComboBox(parent); - comboBox->setView(new QListView(comboBox)); - comboBox->setMaxVisibleItems(10); - comboBox->setStyleSheet(comboBoxStyleSheet); - return comboBox; -} - AudioEncoderDelegate::AudioEncoderDelegate(QObject *parent) : QStyledItemDelegate(parent) {} @@ -25,7 +16,7 @@ auto AudioEncoderDelegate::createEditor(QWidget *parent, { static auto audioEncodercs = Ffmpeg::getCodecsInfo(AVMEDIA_TYPE_AUDIO, true); - auto *comboBox = createComboBox(parent); + auto *comboBox = CommonWidgets::createComboBox(parent); for (const auto &codec : std::as_const(audioEncodercs)) { comboBox->addItem(codec.displayName, QVariant::fromValue(codec)); } @@ -54,7 +45,7 @@ auto ChannelLayoutDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const -> QWidget * { - auto *comboBox = createComboBox(parent); + auto *comboBox = CommonWidgets::createComboBox(parent); auto data = index.data(Qt::UserRole).value(); for (const auto &chLayout : std::as_const(data.chLayouts)) { comboBox->addItem(chLayout.channelName, chLayout.channel); @@ -84,7 +75,7 @@ auto ProfileDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const -> QWidget * { - auto *comboBox = createComboBox(parent); + auto *comboBox = CommonWidgets::createComboBox(parent); auto data = index.data(Qt::UserRole).value(); for (const auto &profile : std::as_const(data.profiles)) { comboBox->addItem(profile.name, profile.profile); @@ -114,7 +105,7 @@ auto SampleRateDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const -> QWidget * { - auto *comboBox = createComboBox(parent); + auto *comboBox = CommonWidgets::createComboBox(parent); auto data = index.data(Qt::UserRole).value(); for (const auto &sampleRate : std::as_const(data.sampleRates)) { comboBox->addItem(QString::number(sampleRate), sampleRate); @@ -140,14 +131,14 @@ RemovedDelegate::RemovedDelegate(QObject *parent) : QStyledItemDelegate(parent) {} -bool RemovedDelegate::editorEvent(QEvent *event, +auto RemovedDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, - const QModelIndex &index) + const QModelIndex &index) -> bool { if (event->type() == QEvent::MouseButtonRelease) { - auto e = static_cast(event); - if (e) { + auto *e = static_cast(event); + if (e != nullptr) { emit removed(index); qDebug() << "remove"; } diff --git a/examples/transcoder/transcoder.pro b/examples/transcoder/transcoder.pro index 7e87cf4..e45d6d7 100644 --- a/examples/transcoder/transcoder.pro +++ b/examples/transcoder/transcoder.pro @@ -18,6 +18,7 @@ SOURCES += \ audioencodermodel.cc \ audioencodertableview.cc \ audioencoderwidget.cc \ + commonwidgets.cc \ main.cc \ mainwindow.cc \ outputwidget.cc \ @@ -34,6 +35,7 @@ HEADERS += \ audioencodermodel.hpp \ audioencodertableview.hpp \ audioencoderwidget.hpp \ + commonwidgets.hpp \ mainwindow.hpp \ outputwidget.hpp \ previewwidget.hpp \ diff --git a/examples/transcoder/videoencoderwidget.cc b/examples/transcoder/videoencoderwidget.cc index 7fc77f4..c6bf230 100644 --- a/examples/transcoder/videoencoderwidget.cc +++ b/examples/transcoder/videoencoderwidget.cc @@ -1,4 +1,5 @@ #include "videoencoderwidget.hpp" +#include "commonwidgets.hpp" #include #include @@ -12,11 +13,7 @@ class VideoEncoderWidget::VideoEncoderWidgetPrivate 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); + videoEncoderCbx = CommonWidgets::createComboBox(q_ptr); auto videoCodecs = Ffmpeg::getCodecsInfo(AVMEDIA_TYPE_VIDEO, true); for (const auto &codec : std::as_const(videoCodecs)) { videoEncoderCbx->addItem(codec.displayName, QVariant::fromValue(codec)); @@ -111,7 +108,7 @@ class VideoEncoderWidget::VideoEncoderWidgetPrivate QSpinBox *maxBitrateSbx; QSpinBox *bitrateSbx; - QSize originalSize; + Ffmpeg::EncodeContext decodeContext; }; VideoEncoderWidget::VideoEncoderWidget(QWidget *parent) @@ -127,20 +124,25 @@ VideoEncoderWidget::~VideoEncoderWidget() = default; auto VideoEncoderWidget::encodeContext() const -> Ffmpeg::EncodeContext { - Ffmpeg::EncodeContext encodeParam; - encodeParam.mediaType = AVMEDIA_TYPE_VIDEO; - encodeParam.setEncoderName(d_ptr->currentCodecName()); - encodeParam.size = {d_ptr->widthSbx->value(), d_ptr->heightSbx->value()}; - encodeParam.gpuDecode = d_ptr->gpuDecodeCbx->isChecked(); - encodeParam.minBitrate = d_ptr->minBitrateSbx->value(); - encodeParam.maxBitrate = d_ptr->maxBitrateSbx->value(); - encodeParam.bitrate = d_ptr->bitrateSbx->value(); - encodeParam.crf = d_ptr->crfSbx->value(); - encodeParam.preset = d_ptr->presetCbx->currentText(); - encodeParam.tune = d_ptr->tuneCbx->currentText(); - // encodeParam.profile = d_ptr->profileCbx->currentText(); - - return encodeParam; + Ffmpeg::EncodeContext encodeContext; + encodeContext.streamIndex = d_ptr->decodeContext.streamIndex; + encodeContext.mediaType = AVMEDIA_TYPE_VIDEO; + encodeContext.setEncoderName(d_ptr->currentCodecName()); + encodeContext.size = {d_ptr->widthSbx->value(), d_ptr->heightSbx->value()}; + encodeContext.minBitrate = d_ptr->minBitrateSbx->value(); + encodeContext.maxBitrate = d_ptr->maxBitrateSbx->value(); + encodeContext.bitrate = d_ptr->bitrateSbx->value(); + encodeContext.crf = d_ptr->crfSbx->value(); + encodeContext.preset = d_ptr->presetCbx->currentText(); + encodeContext.tune = d_ptr->tuneCbx->currentText(); + // encodeContext.profile = d_ptr->profileCbx->currentText(); + + return encodeContext; +} + +bool VideoEncoderWidget::isGpuDecode() const +{ + return d_ptr->gpuDecodeCbx->isChecked(); } void VideoEncoderWidget::setDecodeContext(const Ffmpeg::EncodeContext &decodeContext) @@ -158,8 +160,6 @@ void VideoEncoderWidget::setDecodeContext(const Ffmpeg::EncodeContext &decodeCon d_ptr->widthSbx->blockSignals(false); d_ptr->heightSbx->blockSignals(false); - d_ptr->originalSize = decodeContext.size; - if (decodeContext.maxBitrate <= 0) { d_ptr->calBitrate(); } else { @@ -167,6 +167,8 @@ void VideoEncoderWidget::setDecodeContext(const Ffmpeg::EncodeContext &decodeCon d_ptr->maxBitrateSbx->setValue(decodeContext.maxBitrate); d_ptr->bitrateSbx->setValue(decodeContext.bitrate); } + + d_ptr->decodeContext = decodeContext; } void VideoEncoderWidget::onEncoderChanged() @@ -185,11 +187,11 @@ void VideoEncoderWidget::onEncoderChanged() void VideoEncoderWidget::onVideoWidthChanged() { - if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->originalSize.isValid()) { + if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->decodeContext.size.isValid()) { return; } - auto multiple = d_ptr->originalSize.width() * 1.0 / d_ptr->widthSbx->value(); - int height = d_ptr->originalSize.height() / multiple; + auto multiple = d_ptr->decodeContext.size.width() * 1.0 / d_ptr->widthSbx->value(); + int height = d_ptr->decodeContext.size.height() / multiple; d_ptr->heightSbx->blockSignals(true); d_ptr->heightSbx->setValue(height); d_ptr->heightSbx->blockSignals(false); @@ -198,11 +200,11 @@ void VideoEncoderWidget::onVideoWidthChanged() void VideoEncoderWidget::onVideoHeightChanged() { - if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->originalSize.isValid()) { + if (!d_ptr->aspectCheckBox->isChecked() || !d_ptr->decodeContext.size.isValid()) { return; } - auto multiple = d_ptr->originalSize.height() * 1.0 / d_ptr->heightSbx->value(); - int width = d_ptr->originalSize.width() / multiple; + auto multiple = d_ptr->decodeContext.size.height() * 1.0 / d_ptr->heightSbx->value(); + int width = d_ptr->decodeContext.size.width() / multiple; d_ptr->widthSbx->blockSignals(true); d_ptr->widthSbx->setValue(width); d_ptr->widthSbx->blockSignals(false); diff --git a/examples/transcoder/videoencoderwidget.hpp b/examples/transcoder/videoencoderwidget.hpp index 375890e..296281f 100644 --- a/examples/transcoder/videoencoderwidget.hpp +++ b/examples/transcoder/videoencoderwidget.hpp @@ -19,6 +19,8 @@ class VideoEncoderWidget : public QWidget void setDecodeContext(const Ffmpeg::EncodeContext &decodeContext); [[nodiscard]] auto encodeContext() const -> Ffmpeg::EncodeContext; + bool isGpuDecode() const; + private slots: void onEncoderChanged(); void onVideoWidthChanged(); diff --git a/ffmpeg/codeccontext.cpp b/ffmpeg/codeccontext.cpp index c46d5f6..f3873df 100644 --- a/ffmpeg/codeccontext.cpp +++ b/ffmpeg/codeccontext.cpp @@ -83,7 +83,7 @@ class CodecContext::CodecContextPrivate codecCtx->global_quality = FF_QP2LAMBDA * crf + 0.5; auto quality = QString::number(crf, 'f', 2); av_dict_set(&encodeOptions, "crf", quality.toUtf8().data(), 0); - } else if (codecName.contains("nvenc")) { // nvidia编码器 + } else if (codecName.contains("_nvenc")) { // nvidia编码器 double adjustedQualityI = crf - 2; double adjustedQualityB = crf + 2; if (adjustedQualityB > EncodeLimit::crf_max) { @@ -105,7 +105,7 @@ class CodecContext::CodecContextPrivate av_dict_set(&encodeOptions, "init_qpP", quality.toUtf8().data(), 0); av_dict_set(&encodeOptions, "init_qpB", qualityB.toUtf8().data(), 0); av_dict_set(&encodeOptions, "init_qpI", qualityI.toUtf8().data(), 0); - } else if (codecName.contains("vce")) { // amd vce编码器 + } else if (codecName.contains("_vce")) { // amd vce编码器 int maxQuality = EncodeLimit::crf_max; double qualityOffsetThreshold = 8; double qualityOffsetP = 2; @@ -145,12 +145,11 @@ class CodecContext::CodecContextPrivate if (codecCtx->codec_id != AV_CODEC_ID_H265) { av_dict_set(&encodeOptions, "qp_b", qualityB.toUtf8().data(), 0); } - } else if (codecName.contains("mf")) { // ffmpeg mf编码器 - + } else if (codecName.contains("_mf")) { // ffmpeg mf编码器 auto quality = QString::number(crf, 'f', 2); av_dict_set(&encodeOptions, "rate_control", "quality", 0); av_dict_set(&encodeOptions, "quality", quality.toUtf8().data(), 0); - av_dict_set(&encodeOptions, "hw_encoding", "1", 0); + // av_dict_set(&encodeOptions, "hw_encoding", "1", 0); } else { codecCtx->flags |= AV_CODEC_FLAG_QSCALE; codecCtx->global_quality = FF_QP2LAMBDA * crf + 0.5; diff --git a/ffmpeg/encodecontext.cc b/ffmpeg/encodecontext.cc index 75bc660..c365151 100644 --- a/ffmpeg/encodecontext.cc +++ b/ffmpeg/encodecontext.cc @@ -15,7 +15,7 @@ EncodeContext::EncodeContext(AVStream *stream, AVContextInfo *info) { auto *avCodecContext = info->codecCtx()->avCodecCtx(); const auto *codec = avCodecContext->codec; - this->streamIndex = stream->index; + streamIndex = stream->index; mediaType = avCodecContext->codec_type; minBitrate = avCodecContext->rc_min_rate; maxBitrate = avCodecContext->rc_max_rate; diff --git a/ffmpeg/encodecontext.hpp b/ffmpeg/encodecontext.hpp index 21e943d..2f63388 100644 --- a/ffmpeg/encodecontext.hpp +++ b/ffmpeg/encodecontext.hpp @@ -57,7 +57,7 @@ struct FFMPEG_EXPORT EncodeContext QString sourceInfo; int streamIndex = -1; - AVMediaType mediaType; + AVMediaType mediaType = AVMEDIA_TYPE_UNKNOWN; qint64 minBitrate = -1; qint64 maxBitrate = -1; @@ -67,7 +67,6 @@ struct FFMPEG_EXPORT EncodeContext int crf = 18; // video - bool gpuDecode = true; QSize size = {-1, -1}; QString preset = "slow"; QString tune = "film"; diff --git a/ffmpeg/ffmpegutils.cc b/ffmpeg/ffmpegutils.cc index 2679e19..a5bdabf 100644 --- a/ffmpeg/ffmpegutils.cc +++ b/ffmpeg/ffmpegutils.cc @@ -233,4 +233,11 @@ auto getChLayouts(const QVector &channelLayout) -> ChLayouts return chLayouts.isEmpty() ? s_chLayouts : chLayouts; } +QByteArray convertUrlToFfmpegInput(const QString &url) +{ + QByteArray inpuUrl = QUrl::fromUserInput(url).isLocalFile() ? url.toUtf8() + : QUrl(url).toEncoded(); + return inpuUrl; +} + } // namespace Ffmpeg diff --git a/ffmpeg/ffmpegutils.hpp b/ffmpeg/ffmpegutils.hpp index 237fec2..542a668 100644 --- a/ffmpeg/ffmpegutils.hpp +++ b/ffmpeg/ffmpegutils.hpp @@ -61,6 +61,8 @@ using ChLayouts = QVector; auto FFMPEG_EXPORT getChLayouts(const QVector &channelLayout) -> ChLayouts; +auto convertUrlToFfmpegInput(const QString &url) -> QByteArray; + } // namespace Ffmpeg Q_DECLARE_METATYPE(Ffmpeg::CodecInfo); diff --git a/ffmpeg/filter/filter.cc b/ffmpeg/filter/filter.cc index 9466e19..81abaf8 100644 --- a/ffmpeg/filter/filter.cc +++ b/ffmpeg/filter/filter.cc @@ -40,7 +40,7 @@ class Filter::FilterPrivate timeBase.den, sampleAspectRatio.num, sampleAspectRatio.den); - qDebug() << "Video filter in args:" << args; + qInfo() << "Video filter in args:" << args; create(args); } @@ -60,7 +60,7 @@ class Filter::FilterPrivate avFrame->sample_rate, av_get_sample_fmt_name(static_cast(avFrame->format)), getAVChannelLayoutDescribe(avFrame->ch_layout).toUtf8().data()); - qDebug() << "Audio filter in args:" << args; + qInfo() << "Audio filter in args:" << args; create(args); } diff --git a/ffmpeg/formatcontext.cpp b/ffmpeg/formatcontext.cpp index 96f49a7..1bd9812 100644 --- a/ffmpeg/formatcontext.cpp +++ b/ffmpeg/formatcontext.cpp @@ -1,5 +1,6 @@ #include "formatcontext.h" #include "averrormanager.hpp" +#include "ffmpegutils.hpp" #include "packet.h" #include @@ -150,14 +151,9 @@ auto FormatContext::openFilePath(const QString &filepath, OpenMode mode) -> bool close(); d_ptr->filepath = filepath; - QByteArray inpuUrl; - if (QFile::exists(d_ptr->filepath)) { - inpuUrl = d_ptr->filepath.toUtf8(); - } else { - inpuUrl = QUrl(d_ptr->filepath).toEncoded(); - } - //初始化pFormatCtx结构 - if (mode == ReadOnly) { + auto inpuUrl = convertUrlToFfmpegInput(d_ptr->filepath); + switch (mode) { + case ReadOnly: { auto ret = avformat_open_input(&d_ptr->formatCtx, inpuUrl.constData(), nullptr, nullptr); if (ret != 0) { SET_ERROR_CODE(ret); @@ -165,7 +161,8 @@ auto FormatContext::openFilePath(const QString &filepath, OpenMode mode) -> bool } av_format_inject_global_side_data(d_ptr->formatCtx); d_ptr->isOpen = true; - } else if (mode == WriteOnly) { + } break; + case WriteOnly: { avformat_free_context(d_ptr->formatCtx); auto ret = avformat_alloc_output_context2(&d_ptr->formatCtx, nullptr, @@ -176,6 +173,8 @@ auto FormatContext::openFilePath(const QString &filepath, OpenMode mode) -> bool return false; } d_ptr->isOpen = true; + } break; + default: break; } return d_ptr->isOpen; } @@ -202,9 +201,8 @@ auto FormatContext::avioOpen() -> bool if ((d_ptr->formatCtx->oformat->flags & AVFMT_NOFILE) != 0) { return false; } - auto ret = ::avio_open(&d_ptr->formatCtx->pb, - d_ptr->filepath.toLocal8Bit().constData(), - AVIO_FLAG_WRITE); + auto inpuUrl = convertUrlToFfmpegInput(d_ptr->filepath); + auto ret = ::avio_open(&d_ptr->formatCtx->pb, inpuUrl.constData(), AVIO_FLAG_WRITE); ERROR_RETURN(ret) } diff --git a/ffmpeg/transcoder.cc b/ffmpeg/transcoder.cc index 82b7050..516d546 100644 --- a/ffmpeg/transcoder.cc +++ b/ffmpeg/transcoder.cc @@ -29,6 +29,16 @@ extern "C" { namespace Ffmpeg { +static void copyStreamInfo(AVStream *dst, const AVStream *src) +{ + av_dict_copy(&dst->metadata, src->metadata, 0); + dst->disposition = src->disposition; + dst->discard = src->discard; + dst->sample_aspect_ratio = src->sample_aspect_ratio; + dst->avg_frame_rate = src->avg_frame_rate; + dst->event_flags = src->event_flags; +} + class Transcoder::TranscoderPrivate { public: @@ -41,9 +51,6 @@ class Transcoder::TranscoderPrivate threadPool = new QThreadPool(q_ptr); threadPool->setMaxThreadCount(2); - videoEncodeContext.mediaType = AVMEDIA_TYPE_VIDEO; - audioEncodeContext.mediaType = AVMEDIA_TYPE_AUDIO; - QObject::connect(AVErrorManager::instance(), &AVErrorManager::error, q_ptr, @@ -86,9 +93,8 @@ class Transcoder::TranscoderPrivate return false; } if (codec_type == AVMEDIA_TYPE_VIDEO || codec_type == AVMEDIA_TYPE_AUDIO) { - if (!contextInfoPtr->openCodec(videoEncodeContext.gpuDecode - ? AVContextInfo::GpuDecode - : AVContextInfo::NotUseGpu)) { + if (!contextInfoPtr->openCodec(gpuDecode ? AVContextInfo::GpuDecode + : AVContextInfo::NotUseGpu)) { return false; } } @@ -110,7 +116,7 @@ class Transcoder::TranscoderPrivate return true; } - auto openOutputFile() -> bool + auto openOutputFile() const -> bool { Q_ASSERT(!outFilepath.isEmpty()); auto ret = outFormatContext->openFilePath(outFilepath, FormatContext::WriteOnly); @@ -119,19 +125,25 @@ class Transcoder::TranscoderPrivate } outFormatContext->copyChapterFrom(inFormatContext); auto stream_num = inFormatContext->streams(); + int outStreamIndex = 0; for (int i = 0; i < stream_num; i++) { + auto encodeContext = encodeContexts.at(i); + + auto *transContext = transcodeContexts.at(i); + transContext->vaild = (encodeContext.streamIndex >= 0); + if (!transContext->vaild) { + continue; + } + transContext->outStreamIndex = outStreamIndex; + outStreamIndex++; + auto *inStream = inFormatContext->stream(i); auto *stream = outFormatContext->createStream(); if (stream == nullptr) { return false; } - av_dict_copy(&stream->metadata, inStream->metadata, 0); - stream->disposition = inStream->disposition; - stream->discard = inStream->discard; - stream->sample_aspect_ratio = inStream->sample_aspect_ratio; - stream->avg_frame_rate = inStream->avg_frame_rate; - stream->event_flags = inStream->event_flags; - auto *transContext = transcodeContexts.at(i); + copyStreamInfo(stream, inStream); + auto decContextInfo = transContext->decContextInfoPtr; if ((inStream->disposition & AV_DISPOSITION_ATTACHED_PIC) != 0) { auto ret = avcodec_parameters_copy(stream->codecpar, inStream->codecpar); @@ -142,10 +154,10 @@ class Transcoder::TranscoderPrivate stream->time_base = inStream->time_base; stream->codecpar->width = inStream->codecpar->width > 0 ? inStream->codecpar->width - : videoEncodeContext.size.width(); + : encodeContext.size.width(); stream->codecpar->height = inStream->codecpar->height > 0 ? inStream->codecpar->height - : videoEncodeContext.size.width(); + : encodeContext.size.width(); continue; } stream->codecpar->codec_type = decContextInfo->mediaType(); @@ -153,20 +165,15 @@ class Transcoder::TranscoderPrivate case AVMEDIA_TYPE_AUDIO: case AVMEDIA_TYPE_VIDEO: { QSharedPointer contextInfoPtr(new AVContextInfo); - contextInfoPtr->setIndex(i); + contextInfoPtr->setIndex(transContext->outStreamIndex); contextInfoPtr->setStream(stream); - contextInfoPtr->initEncoder(decContextInfo->mediaType() == AVMEDIA_TYPE_AUDIO - ? audioEncodeContext.codecInfo().name - : videoEncodeContext.codecInfo().name); - //contextInfoPtr->initEncoder(decContextInfo->codecCtx()->avCodecCtx()->codec_id); + contextInfoPtr->initEncoder(encodeContext.codecInfo().name); auto *codecCtx = contextInfoPtr->codecCtx(); auto *avCodecCtx = codecCtx->avCodecCtx(); decContextInfo->codecCtx()->copyToCodecParameters(codecCtx); // ffmpeg example transcoding.c ? framerate, sample_rate codecCtx->avCodecCtx()->time_base = decContextInfo->timebase(); - codecCtx->setEncodeParameters(decContextInfo->mediaType() == AVMEDIA_TYPE_AUDIO - ? audioEncodeContext - : videoEncodeContext); + codecCtx->setEncodeParameters(encodeContext); if ((outFormatContext->avFormatContext()->oformat->flags & AVFMT_GLOBALHEADER) != 0) { avCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; @@ -197,7 +204,9 @@ class Transcoder::TranscoderPrivate } } outFormatContext->dumpFormat(); - outFormatContext->avioOpen(); + if (!outFormatContext->avioOpen()) { + return false; + } return outFormatContext->writeHeader(); } @@ -213,10 +222,11 @@ class Transcoder::TranscoderPrivate } QString filter_spec; if (codec_type == AVMEDIA_TYPE_VIDEO) { - if (videoEncodeContext.size.isValid()) { // "scale=320:240" + auto encodeContext = decodeContexts.at(stream_index); + if (encodeContext.size.isValid()) { // "scale=320:240" filter_spec = QString("scale=%1:%2") - .arg(QString::number(videoEncodeContext.size.width()), - QString::number(videoEncodeContext.size.height())); + .arg(QString::number(encodeContext.size.width()), + QString::number(encodeContext.size.height())); } if (!subtitleFilename.isEmpty()) { // "subtitles=filename=..." burn subtitle into video @@ -225,8 +235,8 @@ class Transcoder::TranscoderPrivate } filter_spec += QString("subtitles=filename='%1':original_size=%2x%3") .arg(subtitleFilename, - QString::number(videoEncodeContext.size.width()), - QString::number(videoEncodeContext.size.height())); + QString::number(encodeContext.size.width()), + QString::number(encodeContext.size.height())); } if (filter_spec.isEmpty()) { filter_spec = "null"; @@ -243,7 +253,7 @@ class Transcoder::TranscoderPrivate auto stream_num = inFormatContext->streams(); for (int i = 0; i < stream_num; i++) { auto *transCtx = transcodeContexts.at(i); - if (transCtx->decContextInfoPtr.isNull()) { + if (!transCtx->vaild || transCtx->decContextInfoPtr.isNull()) { continue; } if (transCtx->decContextInfoPtr->mediaType() == AVMEDIA_TYPE_AUDIO) { @@ -369,10 +379,13 @@ class Transcoder::TranscoderPrivate packetPtrs = transcodeCtx->encContextInfoPtr->encodeFrame(framePtr); } for (const auto &packetPtr : packetPtrs) { - packetPtr->setStreamIndex(stream_index); + packetPtr->setStreamIndex(transcodeCtx->outStreamIndex); packetPtr->rescaleTs(transcodeCtx->encContextInfoPtr->timebase(), - outFormatContext->stream(stream_index)->time_base); + outFormatContext->stream(transcodeCtx->outStreamIndex)->time_base); outFormatContext->writePacket(packetPtr.data()); + if (transcodeCtx->outStreamIndex == 6 || transcodeCtx->outStreamIndex == 10) { + qDebug() << "write packet: " << packetPtr->streamIndex(); + } } return true; } @@ -403,14 +416,21 @@ class Transcoder::TranscoderPrivate } auto stream_index = packetPtr->streamIndex(); auto *transcodeCtx = transcodeContexts.at(stream_index); + if (!transcodeCtx->vaild) { + continue; + } + + auto decContextInfoPtr = transcodeCtx->decContextInfoPtr; auto encContextInfoPtr = transcodeCtx->encContextInfoPtr; + auto inTimebase = inFormatContext->stream(stream_index)->time_base; if (encContextInfoPtr.isNull()) { - packetPtr->rescaleTs(inFormatContext->stream(stream_index)->time_base, - outFormatContext->stream(stream_index)->time_base); + packetPtr + ->rescaleTs(inTimebase, + outFormatContext->stream(transcodeCtx->outStreamIndex)->time_base); + packetPtr->setStreamIndex(transcodeCtx->outStreamIndex); outFormatContext->writePacket(packetPtr.data()); } else { - packetPtr->rescaleTs(inFormatContext->stream(stream_index)->time_base, - transcodeCtx->decContextInfoPtr->timebase()); + packetPtr->rescaleTs(inTimebase, transcodeCtx->decContextInfoPtr->timebase()); auto framePtrs = transcodeCtx->decContextInfoPtr->decodeFrame(packetPtr); for (const auto &framePtr : framePtrs) { if (!transcodeCtx->filterPtr->isInitialized()) { @@ -419,8 +439,7 @@ class Transcoder::TranscoderPrivate filterEncodeWriteframe(framePtr.data(), stream_index); } - calculatePts(packetPtr.data(), - transcodeContexts.at(stream_index)->decContextInfoPtr.data()); + calculatePts(packetPtr.data(), decContextInfoPtr.data()); addPropertyChangeEvent(new PositionEvent(packetPtr->pts())); if (transcodeCtx->decContextInfoPtr->mediaType() == AVMEDIA_TYPE_VIDEO) { fpsPtr->update(); @@ -490,20 +509,21 @@ class Transcoder::TranscoderPrivate QVector transcodeContexts{}; QString subtitleFilename; - EncodeContext videoEncodeContext; - EncodeContext audioEncodeContext; QPair range; + EncodeContexts decodeContexts; + EncodeContexts encodeContexts; + std::atomic_bool runing = true; QScopedPointer fpsPtr; + bool gpuDecode = true; + Utils::ThreadSafeQueue propertyChangeEventQueue; std::atomic maxPropertyEventQueueSize = 100; std::vector previewFrames; QThreadPool *threadPool; - - EncodeContexts decodeContexts; }; Transcoder::Transcoder(QObject *parent) @@ -568,14 +588,23 @@ void Transcoder::setOutFilePath(const QString &filepath) d_ptr->outFilepath = filepath; } -void Transcoder::setVideoEncodeContext(const EncodeContext &encodeContext) +void Transcoder::setGpuDecode(bool enable) +{ + d_ptr->gpuDecode = enable; +} + +auto Transcoder::isGpuDecode() -> bool { - d_ptr->videoEncodeContext = encodeContext; + return d_ptr->gpuDecode; } -void Transcoder::setAudioEncodeContext(const EncodeContext &encodeContext) +void Transcoder::setEncodeContexts(const EncodeContexts &encodeContexts) { - d_ptr->audioEncodeContext = encodeContext; + for (int i = 0; i < encodeContexts.size(); i++) { + auto index = encodeContexts.at(i).streamIndex; + Q_ASSERT(i == index || index < 0); + } + d_ptr->encodeContexts = encodeContexts; } auto Transcoder::decodeContexts() const -> EncodeContexts diff --git a/ffmpeg/transcoder.hpp b/ffmpeg/transcoder.hpp index 53e3aa3..b55d7a2 100644 --- a/ffmpeg/transcoder.hpp +++ b/ffmpeg/transcoder.hpp @@ -26,21 +26,22 @@ class FFMPEG_EXPORT Transcoder : public QThread void setInFilePath(const QString &filePath); void parseInputFile(); + void setOutFilePath(const QString &filepath); + + void setGpuDecode(bool enable); + auto isGpuDecode() -> bool; + + void setEncodeContexts(const EncodeContexts &encodeContexts); + [[nodiscard]] auto decodeContexts() const -> EncodeContexts; + [[nodiscard]] auto duration() const -> qint64; // microsecond auto mediaInfo() -> MediaInfo; void startPreviewFrames(int count); void setPreviewFrames(const std::vector> &framePtrs); [[nodiscard]] auto previewFrames() const -> std::vector>; - void setVideoEncodeContext(const EncodeContext &encodeContext); - void setAudioEncodeContext(const EncodeContext &encodeContext); - - [[nodiscard]] auto decodeContexts() const -> EncodeContexts; - void setRange(const QPair &range); - void setOutFilePath(const QString &filepath); - void setSubtitleFilename(const QString &filename); void startTranscode(); diff --git a/ffmpeg/transcodercontext.hpp b/ffmpeg/transcodercontext.hpp index 6b5a6b2..b2abbb7 100644 --- a/ffmpeg/transcodercontext.hpp +++ b/ffmpeg/transcodercontext.hpp @@ -21,6 +21,9 @@ struct TranscoderContext QSharedPointer audioFifoPtr; qint64 audioPts = 0; + + bool vaild = false; + int outStreamIndex = -1; }; } // namespace Ffmpeg