diff --git a/dialogs/advancedvideodialog.cpp b/dialogs/advancedvideodialog.cpp new file mode 100644 index 000000000..da5b7a366 --- /dev/null +++ b/dialogs/advancedvideodialog.cpp @@ -0,0 +1,70 @@ +#include "advancedvideodialog.h" + +#include +#include +#include +#include + +#include + +extern "C" { + #include + #include +} + +AdvancedVideoDialog::AdvancedVideoDialog(QWidget *parent, + int encoding_codec, + VideoCodecParams &iparams) : + QDialog(parent), + params(iparams) +{ + setWindowTitle(tr("Advanced Video Settings")); + + // use variable for row to assist adding new fields to this dialog + int row = 0; + + // get encoder information for this codec + AVCodec* codec_info = avcodec_find_encoder(static_cast(encoding_codec)); + + // set up grid layout for dialog + QGridLayout* layout = new QGridLayout(this); + + // codec pixel formats + layout->addWidget(new QLabel(tr("Pixel Format:")), row, 0); + + pix_fmt_combo = new QComboBox(); + + // load possible pixel formats for this codec into the combobox + int pix_fmt_index = 0; + + // AVCodec->pix_fmts is terminated by "-1" + while (codec_info->pix_fmts[pix_fmt_index] != -1) { + pix_fmt_combo->addItem(av_get_pix_fmt_name(codec_info->pix_fmts[pix_fmt_index]), + codec_info->pix_fmts[pix_fmt_index]); + + if (codec_info->pix_fmts[pix_fmt_index] == params.pix_fmt) { + pix_fmt_combo->setCurrentIndex(pix_fmt_combo->count()-1); + } + + pix_fmt_index++; + } + + layout->addWidget(pix_fmt_combo, row, 1); + + row++; + + // buttons + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttons->setCenterButtons(true); + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); + layout->addWidget(buttons, row, 0, 1, 2); +} + +void AdvancedVideoDialog::accept() { + // store settings back into struct + + params.pix_fmt = pix_fmt_combo->currentData().toInt(); + + QDialog::accept(); +} diff --git a/dialogs/advancedvideodialog.h b/dialogs/advancedvideodialog.h new file mode 100644 index 000000000..a9510aee3 --- /dev/null +++ b/dialogs/advancedvideodialog.h @@ -0,0 +1,24 @@ +#ifndef ADVANCEDVIDEODIALOG_H +#define ADVANCEDVIDEODIALOG_H + +#include + +#include "io/exportthread.h" + +class QComboBox; + +class AdvancedVideoDialog : public QDialog { + Q_OBJECT +public: + AdvancedVideoDialog(QWidget* parent, + int encoding_codec, + VideoCodecParams& iparams); + + void accept(); +private: + VideoCodecParams& params; + + QComboBox* pix_fmt_combo; +}; + +#endif // ADVANCEDVIDEODIALOG_H diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index e1c9991c1..9dfd6f0bb 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -16,6 +16,7 @@ #include #include "debug.h" +#include "dialogs/advancedvideodialog.h" #include "panels/panels.h" #include "panels/viewer.h" #include "panels/timeline.h" @@ -103,10 +104,21 @@ ExportDialog::ExportDialog(QWidget *parent) : ExportDialog::~ExportDialog() {} -void ExportDialog::format_changed(int index) -{ - format_vcodecs.clear(); - format_acodecs.clear(); +void ExportDialog::add_codec_to_combobox(QComboBox* box, enum AVCodecID codec) { + QString codec_name; + + AVCodec* codec_info = avcodec_find_encoder(codec); + + if (codec_info == nullptr) { + codec_name = tr("Unknown codec name %1").arg(static_cast(codec)); + } else { + codec_name = codec_info->long_name; + } + + box->addItem(codec_name, codec); +} + +void ExportDialog::format_changed(int index) { vcodecCombobox->clear(); acodecCombobox->clear(); @@ -114,204 +126,186 @@ void ExportDialog::format_changed(int index) int default_acodec = 0; switch (index) { - case FORMAT_3GPP: - format_vcodecs.append(AV_CODEC_ID_MPEG4); - format_vcodecs.append(AV_CODEC_ID_H264); + case FORMAT_3GPP: + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); - format_acodecs.append(AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); default_vcodec = 1; break; case FORMAT_AIFF: - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); break; case FORMAT_APNG: - format_vcodecs.append(AV_CODEC_ID_APNG); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_APNG); break; case FORMAT_AVI: - format_vcodecs.append(AV_CODEC_ID_H264); - format_vcodecs.append(AV_CODEC_ID_MPEG4); - format_vcodecs.append(AV_CODEC_ID_MJPEG); - format_vcodecs.append(AV_CODEC_ID_MSVIDEO1); - format_vcodecs.append(AV_CODEC_ID_RAWVIDEO); - format_vcodecs.append(AV_CODEC_ID_HUFFYUV); - format_vcodecs.append(AV_CODEC_ID_DVVIDEO); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MSVIDEO1); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_RAWVIDEO); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_HUFFYUV); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_DVVIDEO); - format_acodecs.append(AV_CODEC_ID_AAC); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_FLAC); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_FLAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); default_vcodec = 3; default_acodec = 5; break; case FORMAT_DNXHD: - format_vcodecs.append(AV_CODEC_ID_DNXHD); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_DNXHD); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); break; case FORMAT_AC3: - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_EAC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_EAC3); break; case FORMAT_FLV: - format_vcodecs.append(AV_CODEC_ID_FLV1); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_FLV1); - format_acodecs.append(AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); break; case FORMAT_GIF: - format_vcodecs.append(AV_CODEC_ID_GIF); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_GIF); break; case FORMAT_IMG: - format_vcodecs.append(AV_CODEC_ID_BMP); - format_vcodecs.append(AV_CODEC_ID_MJPEG); - format_vcodecs.append(AV_CODEC_ID_JPEG2000); - format_vcodecs.append(AV_CODEC_ID_PSD); - format_vcodecs.append(AV_CODEC_ID_PNG); - format_vcodecs.append(AV_CODEC_ID_TIFF); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_BMP); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_JPEG2000); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PSD); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PNG); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_TIFF); default_vcodec = 4; break; case FORMAT_MP2: - format_acodecs.append(AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); break; case FORMAT_MP3: - format_acodecs.append(AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); break; case FORMAT_MPEG1: - format_vcodecs.append(AV_CODEC_ID_MPEG1VIDEO); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG1VIDEO); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); default_acodec = 1; break; case FORMAT_MPEG2: - format_vcodecs.append(AV_CODEC_ID_MPEG2VIDEO); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG2VIDEO); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); default_acodec = 1; break; case FORMAT_MPEG4: - format_vcodecs.append(AV_CODEC_ID_MPEG4); - format_vcodecs.append(AV_CODEC_ID_H264); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); - format_acodecs.append(AV_CODEC_ID_AAC); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); default_vcodec = 1; break; case FORMAT_MPEGTS: - format_vcodecs.append(AV_CODEC_ID_MPEG2VIDEO); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG2VIDEO); - format_acodecs.append(AV_CODEC_ID_AAC); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); default_acodec = 2; break; case FORMAT_MKV: - format_vcodecs.append(AV_CODEC_ID_MPEG4); - format_vcodecs.append(AV_CODEC_ID_H264); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); - format_acodecs.append(AV_CODEC_ID_AAC); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_EAC3); - format_acodecs.append(AV_CODEC_ID_FLAC); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); - format_acodecs.append(AV_CODEC_ID_OPUS); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); - format_acodecs.append(AV_CODEC_ID_VORBIS); - format_acodecs.append(AV_CODEC_ID_WAVPACK); - format_acodecs.append(AV_CODEC_ID_WMAV1); - format_acodecs.append(AV_CODEC_ID_WMAV2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_EAC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_FLAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WAVPACK); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV1); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV2); default_vcodec = 1; break; case FORMAT_OGG: - format_vcodecs.append(AV_CODEC_ID_THEORA); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_THEORA); - format_acodecs.append(AV_CODEC_ID_OPUS); - format_acodecs.append(AV_CODEC_ID_VORBIS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS); default_acodec = 1; break; case FORMAT_MOV: - format_vcodecs.append(AV_CODEC_ID_QTRLE); - format_vcodecs.append(AV_CODEC_ID_MPEG4); - format_vcodecs.append(AV_CODEC_ID_H264); - format_vcodecs.append(AV_CODEC_ID_MJPEG); - format_vcodecs.append(AV_CODEC_ID_PRORES); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_QTRLE); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MJPEG); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_PRORES); - format_acodecs.append(AV_CODEC_ID_AAC); - format_acodecs.append(AV_CODEC_ID_AC3); - format_acodecs.append(AV_CODEC_ID_MP2); - format_acodecs.append(AV_CODEC_ID_MP3); - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AAC); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_AC3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_MP3); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); default_vcodec = 2; break; case FORMAT_WAV: - format_acodecs.append(AV_CODEC_ID_PCM_S16LE); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_PCM_S16LE); break; case FORMAT_WEBM: - format_vcodecs.append(AV_CODEC_ID_VP8); - format_vcodecs.append(AV_CODEC_ID_VP9); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_VP8); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_VP9); - format_acodecs.append(AV_CODEC_ID_OPUS); - format_acodecs.append(AV_CODEC_ID_VORBIS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_OPUS); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_VORBIS); default_vcodec = 1; break; case FORMAT_WMV: - format_vcodecs.append(AV_CODEC_ID_WMV1); - format_vcodecs.append(AV_CODEC_ID_WMV2); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_WMV1); + add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_WMV2); - format_acodecs.append(AV_CODEC_ID_WMAV1); - format_acodecs.append(AV_CODEC_ID_WMAV2); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV1); + add_codec_to_combobox(acodecCombobox, AV_CODEC_ID_WMAV2); default_vcodec = 1; default_acodec = 1; break; default: qCritical() << "Invalid format selection - this is a bug, please inform the developers"; - } - - AVCodec* codec_info; - for (int i=0;iaddItem("nullptr"); - } else { - vcodecCombobox->addItem(codec_info->long_name); - } - } - for (int i=0;iaddItem("nullptr"); - } else { - acodecCombobox->addItem(codec_info->long_name); - } - } + } vcodecCombobox->setCurrentIndex(default_vcodec); acodecCombobox->setCurrentIndex(default_acodec); - bool video_enabled = format_vcodecs.size() != 0; - bool audio_enabled = format_acodecs.size() != 0; + bool video_enabled = vcodecCombobox->count() != 0; + bool audio_enabled = acodecCombobox->count() != 0; videoGroupbox->setChecked(video_enabled); audioGroupbox->setChecked(audio_enabled); videoGroupbox->setEnabled(video_enabled); @@ -378,7 +372,7 @@ void ExportDialog::export_action() { ext = "gif"; break; case FORMAT_IMG: - switch (format_vcodecs.at(vcodecCombobox->currentIndex())) { + switch (vcodecCombobox->currentData().toInt()) { case AV_CODEC_ID_BMP: ext = "bmp"; break; @@ -498,7 +492,32 @@ void ExportDialog::export_action() { } } - et = new ExportThread(this); + ExportParams params; + params.filename = filename; + params.video_enabled = videoGroupbox->isChecked(); + if (params.video_enabled) { + params.video_codec = vcodecCombobox->currentData().toInt(); + params.video_width = widthSpinbox->value(); + params.video_height = heightSpinbox->value(); + params.video_frame_rate = framerateSpinbox->value(); + params.video_compression_type = compressionTypeCombobox->currentData().toInt(); + params.video_bitrate = videobitrateSpinbox->value(); + } + params.audio_enabled = audioGroupbox->isChecked(); + if (params.audio_enabled) { + params.audio_codec = acodecCombobox->currentData().toInt(); + params.audio_sampling_rate = samplingRateSpinbox->value(); + params.audio_bitrate = audiobitrateSpinbox->value(); + } + + params.start_frame = 0; + params.end_frame = sequence->getEndFrame(); // entire sequence + if (rangeCombobox->currentIndex() == 1) { + params.start_frame = qMax(sequence->workarea_in, params.start_frame); + params.end_frame = qMin(sequence->workarea_out, params.end_frame); + } + + et = new ExportThread(params, vcodec_params, this); connect(et, SIGNAL(finished()), et, SLOT(deleteLater())); connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished())); @@ -510,31 +529,7 @@ void ExportDialog::export_action() { mainWindow->autorecover_interval(); - prep_ui_for_render(true); - - et->filename = filename; - et->video_enabled = videoGroupbox->isChecked(); - if (et->video_enabled) { - et->video_codec = format_vcodecs.at(vcodecCombobox->currentIndex()); - et->video_width = widthSpinbox->value(); - et->video_height = heightSpinbox->value(); - et->video_frame_rate = framerateSpinbox->value(); - et->video_compression_type = compressionTypeCombobox->currentData().toInt(); - et->video_bitrate = videobitrateSpinbox->value(); - } - et->audio_enabled = audioGroupbox->isChecked(); - if (et->audio_enabled) { - et->audio_codec = format_acodecs.at(acodecCombobox->currentIndex()); - et->audio_sampling_rate = samplingRateSpinbox->value(); - et->audio_bitrate = audiobitrateSpinbox->value(); - } - - et->start_frame = 0; - et->end_frame = sequence->getEndFrame(); // entire sequence - if (rangeCombobox->currentIndex() == 1) { - et->start_frame = qMax(sequence->workarea_in, et->start_frame); - et->end_frame = qMin(sequence->workarea_out, et->end_frame); - } + prep_ui_for_render(true); et->ed = this; cancelled = false; @@ -561,16 +556,34 @@ void ExportDialog::cancel_render() { void ExportDialog::vcodec_changed(int index) { compressionTypeCombobox->clear(); - if ((format_vcodecs.size() > 0 && format_vcodecs.at(index) == AV_CODEC_ID_H264)) { - compressionTypeCombobox->setEnabled(true); - compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR); -// compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE); -// compressionTypeCombobox->addItem("Average bitrate (Two-Pass)", COMPRESSION_TYPE_TARGETBR); - } else { - compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR); - compressionTypeCombobox->setCurrentIndex(0); - compressionTypeCombobox->setEnabled(false); - } + + if (vcodecCombobox->count() > 0) { + if (vcodecCombobox->itemData(index) == AV_CODEC_ID_H264) { + compressionTypeCombobox->setEnabled(true); + compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR); + // compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE); + // compressionTypeCombobox->addItem("Average bitrate (Two-Pass)", COMPRESSION_TYPE_TARGETBR); + } else { + compressionTypeCombobox->addItem(tr("Constant Bitrate"), COMPRESSION_TYPE_CBR); + compressionTypeCombobox->setCurrentIndex(0); + compressionTypeCombobox->setEnabled(false); + } + + // set default pix_fmt for this codec + AVCodec* codec_info = avcodec_find_encoder(static_cast(vcodecCombobox->itemData(index).toInt())); + if (codec_info == nullptr) { + QMessageBox::critical(this, + tr("Invalid Codec"), + tr("Failed to find a suitable encoder for this codec. Export will likely fail.")); + } else { + vcodec_params.pix_fmt = codec_info->pix_fmts[0]; + if (vcodec_params.pix_fmt == -1) { + QMessageBox::critical(this, + tr("Invalid Codec"), + tr("Failed to find pixel format for this encoder. Export will likely fail.")); + } + } + } } void ExportDialog::comp_type_changed(int) { @@ -593,7 +606,12 @@ void ExportDialog::comp_type_changed(int) { videoBitrateLabel->setText(tr("Target File Size (MB):")); videobitrateSpinbox->setValue(100); break; - } + } +} + +void ExportDialog::open_advanced_video_dialog() { + AdvancedVideoDialog avd(this, vcodecCombobox->currentData().toInt(), vcodec_params); + avd.exec(); } void ExportDialog::setup_ui() { @@ -658,6 +676,10 @@ void ExportDialog::setup_ui() { videobitrateSpinbox->setValue(2); videoGridLayout->addWidget(videobitrateSpinbox, 5, 1, 1, 1); + QPushButton* advanced_video_button = new QPushButton(tr("Advanced")); + connect(advanced_video_button, SIGNAL(clicked(bool)), this, SLOT(open_advanced_video_dialog())); + videoGridLayout->addWidget(advanced_video_button, 6, 1); + verticalLayout->addWidget(videoGroupbox); audioGroupbox = new QGroupBox(this); diff --git a/dialogs/exportdialog.h b/dialogs/exportdialog.h index 7e78d2a95..5c0184064 100644 --- a/dialogs/exportdialog.h +++ b/dialogs/exportdialog.h @@ -12,6 +12,8 @@ class QLabel; class QProgressBar; class QGroupBox; +#include "io/exportthread.h" + class ExportDialog : public QDialog { Q_OBJECT @@ -28,17 +30,20 @@ private slots: void render_thread_finished(); void vcodec_changed(int index); void comp_type_changed(int index); + void open_advanced_video_dialog(); private: - QVector format_strings; - QVector format_vcodecs; - QVector format_acodecs; + QVector format_strings; void setup_ui(); ExportThread* et; void prep_ui_for_render(bool r); bool cancelled; + void add_codec_to_combobox(QComboBox* box, enum AVCodecID codec); + + VideoCodecParams vcodec_params; + QComboBox* rangeCombobox; QSpinBox* widthSpinbox; QDoubleSpinBox* videobitrateSpinbox; diff --git a/dialogs/loaddialog.cpp b/dialogs/loaddialog.cpp index 4c666cd82..e0b555df8 100644 --- a/dialogs/loaddialog.cpp +++ b/dialogs/loaddialog.cpp @@ -28,7 +28,7 @@ LoadDialog::LoadDialog(QWidget *parent, bool autorecovery) : QDialog(parent) { cancel_button = new QPushButton(tr("Cancel"), this); connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(cancel())); - hboxLayout = new QHBoxLayout(this); + hboxLayout = new QHBoxLayout(); hboxLayout->addStretch(); hboxLayout->addWidget(cancel_button); hboxLayout->addStretch(); diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp index 41a337610..7b4171a42 100644 --- a/dialogs/replaceclipmediadialog.cpp +++ b/dialogs/replaceclipmediadialog.cpp @@ -39,7 +39,7 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media *old_media use_same_media_in_points->setChecked(true); layout->addWidget(use_same_media_in_points); - QHBoxLayout* buttons = new QHBoxLayout(this); + QHBoxLayout* buttons = new QHBoxLayout(); buttons->addStretch(); diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index d03f6c3e9..d5f411059 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -23,7 +23,7 @@ SpeedDialog::SpeedDialog(QWidget *parent) : QDialog(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); - QGridLayout* grid = new QGridLayout(this); + QGridLayout* grid = new QGridLayout(); grid->setSpacing(6); grid->addWidget(new QLabel(tr("Speed:"), this), 0, 0); diff --git a/io/exportthread.cpp b/io/exportthread.cpp index 190a3e0a4..074327d88 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -27,7 +27,11 @@ extern "C" { #include #include -ExportThread::ExportThread(QObject *parent) : +ExportThread::ExportThread(const ExportParams &iparams, + const VideoCodecParams& ivparams, + QObject *parent) : + params(iparams), + vcodec_params(ivparams), QThread(parent), continueEncode(true) { @@ -80,13 +84,13 @@ bool ExportThread::encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx, bool ExportThread::setupVideo() { // if video is disabled, no setup necessary - if (!video_enabled) return true; + if (!params.video_enabled) return true; // find video encoder - vcodec = avcodec_find_encoder(static_cast(video_codec)); - if (!vcodec) { + vcodec = avcodec_find_encoder(static_cast(params.video_codec)); + if (!vcodec) { qCritical() << "Could not find video encoder"; - ed->export_error = tr("could not video encoder for %1").arg(QString::number(video_codec)); + ed->export_error = tr("could not video encoder for %1").arg(QString::number(params.video_codec)); return false; } @@ -109,14 +113,16 @@ bool ExportThread::setupVideo() { } // setup context - vcodec_ctx->codec_id = static_cast(video_codec); + vcodec_ctx->codec_id = static_cast(params.video_codec); vcodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - vcodec_ctx->width = video_width; - vcodec_ctx->height = video_height; + vcodec_ctx->width = params.video_width; + vcodec_ctx->height = params.video_height; vcodec_ctx->sample_aspect_ratio = {1, 1}; - vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code - vcodec_ctx->framerate = av_d2q(video_frame_rate, INT_MAX); - if (video_compression_type == COMPRESSION_TYPE_CBR) vcodec_ctx->bit_rate = qRound(video_bitrate * 1000000); + vcodec_ctx->pix_fmt = static_cast(vcodec_params.pix_fmt); + vcodec_ctx->framerate = av_d2q(params.video_frame_rate, INT_MAX); + if (params.video_compression_type == COMPRESSION_TYPE_CBR) { + vcodec_ctx->bit_rate = qRound(params.video_bitrate * 1000000); + } vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate); video_stream->time_base = vcodec_ctx->time_base; @@ -126,9 +132,9 @@ bool ExportThread::setupVideo() { switch (vcodec_ctx->codec_id) { case AV_CODEC_ID_H264: - switch (video_compression_type) { + switch (params.video_compression_type) { case COMPRESSION_TYPE_CFR: - av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast(video_bitrate)).toUtf8(), AV_OPT_SEARCH_CHILDREN); + av_opt_set(vcodec_ctx->priv_data, "crf", QString::number(static_cast(params.video_bitrate)).toUtf8(), AV_OPT_SEARCH_CHILDREN); break; } break; @@ -166,8 +172,8 @@ bool ExportThread::setupVideo() { sequence->width, sequence->height, AV_PIX_FMT_RGBA, - video_width, - video_height, + params.video_width, + params.video_height, vcodec_ctx->pix_fmt, SWS_FAST_BILINEAR, nullptr, @@ -180,13 +186,13 @@ bool ExportThread::setupVideo() { bool ExportThread::setupAudio() { // if audio is disabled, no setup necessary - if (!audio_enabled) return true; + if (!params.audio_enabled) return true; // find encoder - acodec = avcodec_find_encoder(static_cast(audio_codec)); + acodec = avcodec_find_encoder(static_cast(params.audio_codec)); if (!acodec) { qCritical() << "Could not find audio encoder"; - ed->export_error = tr("could not audio encoder for %1").arg(QString::number(audio_codec)); + ed->export_error = tr("could not audio encoder for %1").arg(QString::number(params.audio_codec)); return false; } @@ -209,16 +215,16 @@ bool ExportThread::setupAudio() { } // setup context - acodec_ctx->codec_id = static_cast(audio_codec); + acodec_ctx->codec_id = static_cast(params.audio_codec); acodec_ctx->codec_type = AVMEDIA_TYPE_AUDIO; - acodec_ctx->sample_rate = audio_sampling_rate; + acodec_ctx->sample_rate = params.audio_sampling_rate; acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // change this to support surround/mono sound in the future (this is what the user sets the output audio to) acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout); acodec_ctx->sample_fmt = acodec->sample_fmts[0]; - acodec_ctx->bit_rate = audio_bitrate * 1000; + acodec_ctx->bit_rate = params.audio_bitrate * 1000; acodec_ctx->time_base.num = 1; - acodec_ctx->time_base.den = audio_sampling_rate; + acodec_ctx->time_base.den = params.audio_sampling_rate; audio_stream->time_base = acodec_ctx->time_base; if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) { @@ -307,18 +313,18 @@ bool ExportThread::setupContainer() { void ExportThread::run() { panel_sequence_viewer->pause(); - panel_sequence_viewer->seek(start_frame); + panel_sequence_viewer->seek(params.start_frame); // copy filename - QByteArray ba = filename.toUtf8(); + QByteArray ba = params.filename.toUtf8(); c_filename = new char[ba.size()+1]; strcpy(c_filename, ba.data()); continueEncode = setupContainer(); - if (video_enabled && continueEncode) continueEncode = setupVideo(); + if (params.video_enabled && continueEncode) continueEncode = setupVideo(); - if (audio_enabled && continueEncode) continueEncode = setupAudio(); + if (params.audio_enabled && continueEncode) continueEncode = setupAudio(); if (continueEncode) { ret = avformat_write_header(fmt_ctx, nullptr); @@ -339,13 +345,13 @@ void ExportThread::run() { mutex.lock(); - while (sequence->playhead <= end_frame && continueEncode) { + while (sequence->playhead <= params.end_frame && continueEncode) { start_time = QDateTime::currentMSecsSinceEpoch(); - if (audio_enabled) { + if (params.audio_enabled) { compose_audio(nullptr, sequence, true, false); } - if (video_enabled) { + if (params.video_enabled) { do { // TODO optimize by rendering the next frame while encoding the last renderer->start_render(nullptr, sequence, nullptr, video_frame->data[0], video_frame->linesize[0]/4); @@ -356,8 +362,8 @@ void ExportThread::run() { } // encode last frame while rendering next frame - double timecode_secs = (double) (sequence->playhead-start_frame) / sequence->frame_rate; - if (video_enabled) { + double timecode_secs = double(sequence->playhead - params.start_frame) / sequence->frame_rate; + if (params.video_enabled) { // create sws_frame for converting pixel format // @@ -370,8 +376,8 @@ void ExportThread::run() { sws_frame = av_frame_alloc(); sws_frame->format = vcodec_ctx->pix_fmt; - sws_frame->width = video_width; - sws_frame->height = video_height; + sws_frame->width = params.video_width; + sws_frame->height = params.video_height; av_frame_get_buffer(sws_frame, 0); // convert pixel format to format expected by the encoder @@ -383,10 +389,10 @@ void ExportThread::run() { av_frame_free(&sws_frame); } - if (audio_enabled) { + if (params.audio_enabled) { // do we need to encode more audio samples? - while (continueEncode && file_audio_samples <= (timecode_secs*audio_sampling_rate)) { + while (continueEncode && file_audio_samples <= (timecode_secs*params.audio_sampling_rate)) { // copy samples from audio buffer to AVFrame int adjusted_read = audio_ibuffer_read%audio_ibuffer_size; @@ -417,11 +423,11 @@ void ExportThread::run() { // generating encoding statistics (time it took to encode this frame/estimated remaining time) frame_time = (QDateTime::currentMSecsSinceEpoch()-start_time); total_time += frame_time; - remaining_frames = (end_frame-sequence->playhead); + remaining_frames = (params.end_frame - sequence->playhead); avg_time = (total_time/frame_count); eta = (remaining_frames*avg_time); - emit progress_changed(qRound((double(sequence->playhead-start_frame) / double(end_frame-start_frame)) * 100.0), eta); + emit progress_changed(qRound((double(sequence->playhead - params.start_frame) / double(params.end_frame - params.start_frame)) * 100.0), eta); sequence->playhead++; frame_count++; } @@ -432,13 +438,13 @@ void ExportThread::run() { mutex.unlock(); if (continueEncode) { - if (video_enabled) vpkt_alloc = true; - if (audio_enabled) apkt_alloc = true; + if (params.video_enabled) vpkt_alloc = true; + if (params.audio_enabled) apkt_alloc = true; } mainWindow->set_rendering_state(false); - if (audio_enabled && continueEncode) { + if (params.audio_enabled && continueEncode) { // flush swresample do { swr_convert_frame(swr_ctx, swr_frame, nullptr); @@ -454,8 +460,8 @@ void ExportThread::run() { if (continueEncode) { // flush remaining packets while (continueVideo && continueAudio) { - if (continueVideo && video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false); - if (continueAudio && audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true); + if (continueVideo && params.video_enabled) continueVideo = encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false); + if (continueAudio && params.audio_enabled) continueAudio = encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true); } ret = av_write_trailer(fmt_ctx); diff --git a/io/exportthread.h b/io/exportthread.h index ae37ed292..d2ef05486 100644 --- a/io/exportthread.h +++ b/io/exportthread.h @@ -25,28 +25,36 @@ extern "C" { #define COMPRESSION_TYPE_TARGETSIZE 2 #define COMPRESSION_TYPE_TARGETBR 3 +// structs that store parameters passed from the export dialogs to this thread + +struct ExportParams { + // export parameters + QString filename; + bool video_enabled; + int video_codec; + int video_width; + int video_height; + double video_frame_rate; + int video_compression_type; + double video_bitrate; + bool audio_enabled; + int audio_codec; + int audio_sampling_rate; + int audio_bitrate; + long start_frame; + long end_frame; +}; + +struct VideoCodecParams { + int pix_fmt; +}; + class ExportThread : public QThread { Q_OBJECT public: - ExportThread(QObject* parent = nullptr); + ExportThread(const ExportParams& iparams, const VideoCodecParams& ivparams, QObject* parent = nullptr); void run(); - // export parameters - QString filename; - bool video_enabled; - int video_codec; - int video_width; - int video_height; - double video_frame_rate; - int video_compression_type; - double video_bitrate; - bool audio_enabled; - int audio_codec; - int audio_sampling_rate; - int audio_bitrate; - long start_frame; - long end_frame; - QOffscreenSurface surface; ExportDialog* ed; @@ -62,6 +70,10 @@ private: bool setupAudio(); bool setupContainer(); + // params imported from dialogs + ExportParams params; + VideoCodecParams vcodec_params; + AVFormatContext* fmt_ctx; AVStream* video_stream; AVCodec* vcodec; diff --git a/olive.pro b/olive.pro index 9b7f58012..585b0fb40 100644 --- a/olive.pro +++ b/olive.pro @@ -139,7 +139,8 @@ SOURCES += \ effects/internal/vsthost.cpp \ ui/flowlayout.cpp \ dialogs/proxydialog.cpp \ - io/proxygenerator.cpp + io/proxygenerator.cpp \ + dialogs/advancedvideodialog.cpp HEADERS += \ mainwindow.h \ @@ -242,7 +243,8 @@ HEADERS += \ effects/internal/vsthost.h \ ui/flowlayout.h \ dialogs/proxydialog.h \ - io/proxygenerator.h + io/proxygenerator.h \ + dialogs/advancedvideodialog.h FORMS += diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index b635fa3d2..2622e3d51 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -10,7 +10,9 @@ #include LabelSlider::LabelSlider(QWidget* parent) : QLabel(parent) { + // set a default frame rate - fallback, shouldn't ever really be used frame_rate = 30; + decimal_places = 1; drag_start = false; drag_proc = false; @@ -96,7 +98,7 @@ void LabelSlider::set_default_value(double v) { } void LabelSlider::set_minimum_value(double v) { - min_value = v; + min_value = v; min_enabled = true; } @@ -106,47 +108,102 @@ void LabelSlider::set_maximum_value(double v) { } void LabelSlider::mousePressEvent(QMouseEvent *ev) { + // if primary button is clicked if (ev->button() == Qt::LeftButton) { + + // store initial value to be compared while dragging drag_start_value = internal_value; - if (ev->modifiers() & Qt::AltModifier) { + + // alt + click sets labelslider to default value + if (ev->modifiers() & Qt::AltModifier) { + + // if the value is not already default, and there is a default to set if (internal_value != default_value && !qIsNaN(default_value)) { + + // cache current value set_previous_value(); + + // set back to default set_value(default_value, true); + } + } else { + + // if value isn't valid, we set to a "default" of 0 if (qIsNaN(internal_value)) internal_value = 0; + // hide the cursor and store information about it for dragging qApp->setOverrideCursor(Qt::BlankCursor); drag_start = true; drag_start_x = cursor().pos().x(); drag_start_y = cursor().pos().y(); } + emit clicked(); - } + + } else { + + // handler if the user clicks with a non-primary button + // prevents mouse from getting stuck in "dragging" mode + mouseReleaseEvent(ev); + + } } void LabelSlider::mouseMoveEvent(QMouseEvent* event) { if (drag_start) { + // if we're dragging + + // we don't actually start fully dragging until the cursor has moved + // while the mouse is down, this helps prevent accidental drags drag_proc = true; + + // get amount cursor moved by double diff = (cursor().pos().x()-drag_start_x) + (drag_start_y-cursor().pos().y()); + + // ctrl + drag drags in smaller increments if (event->modifiers() & Qt::ControlModifier) diff *= 0.01; + + // we'll also need to drag in smaller increments for a percent value if (display_type == LABELSLIDER_PERCENT) diff *= 0.01; + + // sets the value set_value(internal_value + diff, true); + + // keep the cursor in the same location while dragging cursor().setPos(drag_start_x, drag_start_y); } } -void LabelSlider::mouseReleaseEvent(QMouseEvent* ev) { +void LabelSlider::mouseReleaseEvent(QMouseEvent*) { if (drag_start) { + + // unhide cursor qApp->restoreOverrideCursor(); + drag_start = false; - if (drag_proc) { + + if (drag_proc) { + // if we just finished fully dragging + drag_proc = false; + previous_value = drag_start_value; + emit valueChanged(); + } else { + + // if the user didn't actually drag, and just clicked in one place, + // we instead present a dialog prompt for the user to enter a + // specific value + double d = internal_value; - if (display_type == LABELSLIDER_FRAMENUMBER) { + + if (display_type == LABELSLIDER_FRAMENUMBER) { + + // ask the user to enter a timecode QString s = QInputDialog::getText( this, tr("Set Value"), @@ -155,9 +212,16 @@ void LabelSlider::mouseReleaseEvent(QMouseEvent* ev) { valueToString(internal_value) ); if (s.isEmpty()) return; - d = timecode_to_frame(s, config.timecode_view, frame_rate); // string to frame number + + // parse string timecode to a frame number + d = timecode_to_frame(s, config.timecode_view, frame_rate); + } else { + + // ask the user to enter a normal number value bool ok; + + // percentages are stored 0.0 - 1.0 but displayed as 0% - 100% d = QInputDialog::getDouble( this, tr("Set Value"), @@ -171,6 +235,8 @@ void LabelSlider::mouseReleaseEvent(QMouseEvent* ev) { if (!ok) return; if (display_type == LABELSLIDER_PERCENT) d *= 0.01; } + + // if the value actually changed, trigger a change event if (d != internal_value) { set_previous_value(); set_value(d, true);