Created
November 12, 2020 08:20
-
-
Save xenogenesi/f89d6acbb25bc9cefd4122cb659fa0f0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp | |
index d6899f66..ac13fa0e 100644 | |
--- a/dialogs/exportdialog.cpp | |
+++ b/dialogs/exportdialog.cpp | |
@@ -65,6 +65,9 @@ enum ExportFormats { | |
FORMAT_WAV, | |
FORMAT_WEBM, | |
FORMAT_WMV, | |
+#ifdef EXPORT_POPEN | |
+ FORMAT_RAWPIPE, | |
+#endif | |
FORMAT_SIZE | |
}; | |
@@ -102,6 +105,9 @@ ExportDialog::ExportDialog(QWidget *parent) : | |
format_strings[FORMAT_WAV] = "WAVE Audio"; | |
format_strings[FORMAT_WEBM] = "WebM"; | |
format_strings[FORMAT_WMV] = "Windows Media"; | |
+#ifdef EXPORT_POPEN | |
+ format_strings[FORMAT_RAWPIPE] = "rawvideo to stdin"; | |
+#endif | |
for (int i=0;i<FORMAT_SIZE;i++) { | |
formatCombobox->addItem(format_strings[i]); | |
@@ -140,6 +146,11 @@ void ExportDialog::format_changed(int index) { | |
int default_acodec = 0; | |
switch (index) { | |
+#ifdef EXPORT_POPEN | |
+ case FORMAT_RAWPIPE: | |
+ vcodecCombobox->addItem("rawpipe", COMPRESSION_RAWPIPE); | |
+ break; | |
+#endif | |
case FORMAT_3GPP: | |
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_MPEG4); | |
add_codec_to_combobox(vcodecCombobox, AV_CODEC_ID_H264); | |
@@ -504,6 +515,11 @@ void ExportDialog::StartExport() { | |
ext = "wma"; | |
} | |
break; | |
+#ifdef EXPORT_POPEN | |
+ case FORMAT_RAWPIPE: | |
+ ext = "rpp"; | |
+ break; | |
+#endif | |
default: | |
qCritical() << "Invalid format - this is a bug, please inform the developers"; | |
QMessageBox::critical( | |
@@ -521,8 +537,12 @@ void ExportDialog::StartExport() { | |
format_strings[formatCombobox->currentIndex()] + " (*." + ext + ")" | |
); | |
if (!filename.isEmpty()) { | |
- if (!filename.endsWith("." + ext, Qt::CaseInsensitive)) { | |
- filename += "." + ext; | |
+ if (!filename.endsWith("." + ext, Qt::CaseInsensitive) | |
+#ifdef EXPORT_POPEN | |
+ && formatCombobox->currentIndex() != FORMAT_RAWPIPE | |
+#endif | |
+ ) { | |
+ filename += "." + ext; | |
} | |
if (formatCombobox->currentIndex() == FORMAT_IMG) { | |
@@ -618,7 +638,11 @@ void ExportDialog::vcodec_changed(int index) { | |
if (vcodecCombobox->count() > 0) { | |
if (vcodecCombobox->itemData(index) == AV_CODEC_ID_H264 | |
- || vcodecCombobox->itemData(index) == AV_CODEC_ID_H265) { | |
+ || vcodecCombobox->itemData(index) == AV_CODEC_ID_H265 | |
+#ifdef EXPORT_POPEN | |
+ || vcodecCombobox->itemData(index) == COMPRESSION_RAWPIPE | |
+#endif | |
+ ) { | |
compressionTypeCombobox->setEnabled(true); | |
compressionTypeCombobox->addItem(tr("Quality-based (Constant Rate Factor)"), COMPRESSION_TYPE_CFR); | |
// compressionTypeCombobox->addItem("File size-based (Two-Pass)", COMPRESSION_TYPE_TARGETSIZE); | |
@@ -629,6 +653,11 @@ void ExportDialog::vcodec_changed(int index) { | |
compressionTypeCombobox->setEnabled(false); | |
} | |
+#ifdef EXPORT_POPEN | |
+ if (vcodecCombobox->itemData(index) == COMPRESSION_RAWPIPE) | |
+ return; | |
+#endif | |
+ | |
// set default pix_fmt for this codec | |
AVCodec* codec_info = avcodec_find_encoder(static_cast<AVCodecID>(vcodecCombobox->itemData(index).toInt())); | |
if (codec_info == nullptr) { | |
@@ -670,6 +699,11 @@ void ExportDialog::comp_type_changed(int) { | |
} | |
void ExportDialog::open_advanced_video_dialog() { | |
+#ifdef EXPORT_POPEN | |
+ // FIXME prevent advanced dialog (it crash) but really should be disabled | |
+ if (vcodecCombobox->currentData().toInt() == COMPRESSION_RAWPIPE) | |
+ return; | |
+#endif | |
AdvancedVideoDialog avd(this, static_cast<AVCodecID>(vcodecCombobox->currentData().toInt()), vcodec_params); | |
avd.exec(); | |
} | |
diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp | |
index 9c4dafda..1f388551 100644 | |
--- a/dialogs/preferencesdialog.cpp | |
+++ b/dialogs/preferencesdialog.cpp | |
@@ -52,6 +52,7 @@ | |
#include "ui/columnedgridlayout.h" | |
#include "ui/mainwindow.h" | |
#include "dialogs/newsequencedialog.h" | |
+#include "rendering/exportthread.h" | |
KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) | |
: QKeySequenceEdit(parent), action(a) { | |
@@ -261,6 +262,9 @@ void PreferencesDialog::accept() { | |
olive::CurrentConfig.css_path = custom_css_fn->text(); | |
olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1; | |
olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text(); | |
+#ifdef EXPORT_POPEN | |
+ olive::CurrentConfig.rawpipe_command = rawpipeCommand->text(); | |
+#endif | |
olive::CurrentConfig.upcoming_queue_size = upcoming_queue_spinbox->value(); | |
olive::CurrentConfig.upcoming_queue_type = upcoming_queue_type->currentIndex(); | |
olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value(); | |
@@ -552,6 +556,20 @@ void PreferencesDialog::setup_ui() { | |
row++; | |
+#ifdef EXPORT_POPEN | |
+ | |
+ // General -> Rawpipe command | |
+ general_layout->addWidget(new QLabel(tr("Rawpipe export command:"), this), row, 0); | |
+ | |
+ rawpipeCommand = new QLineEdit(general_tab); | |
+ rawpipeCommand->setText(olive::CurrentConfig.rawpipe_command); | |
+ | |
+ general_layout->addWidget(rawpipeCommand, row, 1, 1, 4); | |
+ | |
+ row++; | |
+ | |
+#endif | |
+ | |
// General -> Thumbnail and Waveform Resolution | |
general_layout->addWidget(new QLabel(tr("Thumbnail Resolution:"), this), row, 0); | |
diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h | |
index e0804bb4..bb9324a7 100644 | |
--- a/dialogs/preferencesdialog.h | |
+++ b/dialogs/preferencesdialog.h | |
@@ -36,6 +36,7 @@ | |
#include <QTimeEdit> | |
#include "timeline/sequence.h" | |
+#include "rendering/exportthread.h" | |
class KeySequenceEditor; | |
@@ -179,6 +180,13 @@ private: | |
*/ | |
QLineEdit* imgSeqFormatEdit; | |
+#ifdef EXPORT_POPEN | |
+ /** | |
+ * @brief UI widget for editing the command used for rawpipe codec export | |
+ */ | |
+ QLineEdit* rawpipeCommand; | |
+#endif | |
+ | |
/** | |
* @brief UI widget for editing the recording channels | |
*/ | |
diff --git a/global/config.cpp b/global/config.cpp | |
index a439ba27..fc8d3b5f 100644 | |
--- a/global/config.cpp | |
+++ b/global/config.cpp | |
@@ -27,6 +27,8 @@ | |
#include "panels/project.h" | |
#include "panels/panels.h" | |
+#include "rendering/exportthread.h" | |
+ | |
#include "debug.h" | |
Config olive::CurrentConfig; | |
@@ -40,6 +42,9 @@ Config::Config() | |
select_also_seeks(false), | |
paste_seeks(true), | |
img_seq_formats("jpg|jpeg|bmp|tiff|tif|psd|png|tga|jp2|gif"), | |
+#ifdef EXPORT_POPEN | |
+ rawpipe_command(""), | |
+#endif | |
rectified_waveforms(false), | |
default_transition_length(30), | |
timecode_view(olive::kTimecodeDrop), | |
@@ -115,6 +120,11 @@ void Config::load(QString path) { | |
} else if (stream.name() == "ImageSequenceFormats") { | |
stream.readNext(); | |
img_seq_formats = stream.text().toString(); | |
+#ifdef EXPORT_POPEN | |
+ } else if (stream.name() == "RawpipeCommand") { | |
+ stream.readNext(); | |
+ rawpipe_command = stream.text().toString(); | |
+#endif | |
} else if (stream.name() == "RectifiedWaveforms") { | |
stream.readNext(); | |
rectified_waveforms = (stream.text() == "1"); | |
@@ -279,6 +289,9 @@ void Config::save(QString path) { | |
stream.writeTextElement("SelectAlsoSeeks", QString::number(select_also_seeks)); | |
stream.writeTextElement("PasteSeeks", QString::number(paste_seeks)); | |
stream.writeTextElement("ImageSequenceFormats", img_seq_formats); | |
+#ifdef EXPORT_POPEN | |
+ stream.writeTextElement("RawpipeCommand", rawpipe_command); | |
+#endif | |
stream.writeTextElement("RectifiedWaveforms", QString::number(rectified_waveforms)); | |
stream.writeTextElement("DefaultTransitionLength", QString::number(default_transition_length)); | |
stream.writeTextElement("TimecodeView", QString::number(timecode_view)); | |
diff --git a/global/config.h b/global/config.h | |
index 77c3487d..8d2b6fd3 100644 | |
--- a/global/config.h | |
+++ b/global/config.h | |
@@ -25,6 +25,7 @@ | |
#include <QTime> | |
#include "ui/styling.h" | |
+#include "rendering/exportthread.h" | |
namespace olive { | |
/** | |
@@ -216,6 +217,16 @@ struct Config { | |
*/ | |
QString img_seq_formats; | |
+#ifdef EXPORT_POPEN | |
+ /** | |
+ * @brief Command to run while exporting with rawpipe codec option | |
+ * | |
+ * Example: ffmpeg-cuda -y -f rawvideo -s {{WIDTH}}x{{HEIGHT}} -pix_fmt rgba -r {{FPS}} -i - | |
+ * -an -c:v h264_nvenc -cq:v {{BITRATE}} -b:v 4000k -maxrate:v 8000k -profile:v high -bf:v 2 {{OUTPUT}} | |
+ */ | |
+ QString rawpipe_command; | |
+#endif | |
+ | |
/** | |
* @brief Use rectified waveforms | |
* | |
diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp | |
index 36cda16b..a89237d7 100644 | |
--- a/rendering/exportthread.cpp | |
+++ b/rendering/exportthread.cpp | |
@@ -42,6 +42,7 @@ extern "C" { | |
#include "rendering/renderfunctions.h" | |
#include "rendering/audio.h" | |
#include "ui/mainwindow.h" | |
+#include "global/config.h" | |
#include "global/debug.h" | |
ExportThread::ExportThread(const ExportParams ¶ms, | |
@@ -106,6 +107,49 @@ bool ExportThread::SetupVideo() { | |
// if video is disabled, no setup necessary | |
if (!params_.video_enabled) return true; | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec == COMPRESSION_RAWPIPE) { | |
+ pipe_frame.format = AV_PIX_FMT_RGBA; | |
+ pipe_frame.width = olive::ActiveSequence->width; | |
+ pipe_frame.height = olive::ActiveSequence->height; | |
+ pipe_frame.data = new unsigned long[pipe_frame.width * pipe_frame.height]; | |
+ for (unsigned long i = 0; i < pipe_frame.width * pipe_frame.height; i++) { | |
+ pipe_frame.data[i] = 0xffff00ff; //ABGR | |
+ } | |
+ | |
+ QString rawpipeCmd = olive::CurrentConfig.rawpipe_command; | |
+ rawpipeCmd.replace("{{WIDTH}}", QString::number(pipe_frame.width)); | |
+ rawpipeCmd.replace("{{HEIGHT}}", QString::number(pipe_frame.height)); | |
+ rawpipeCmd.replace("{{FPS}}", QString::number(params_.video_frame_rate)); | |
+ rawpipeCmd.replace("{{BITRATE}}", QString::number(params_.video_bitrate)); | |
+ rawpipeCmd.replace("{{OUTPUT}}", params_.filename.toUtf8()); | |
+ | |
+ qDebug() << "rawpipe command: " << rawpipeCmd; | |
+ | |
+#if EXPORT_POPEN == 1 | |
+ pipe_frame.file = popen(rawpipeCmd.toUtf8().constData(), "w"); | |
+ if (pipe_frame.file == NULL) { | |
+ qCritical() << "export popen failed"; | |
+ export_error = tr("export popen failed"); | |
+ return false; | |
+ } | |
+#elif EXPORT_POPEN == 2 | |
+ pipe_frame.qproc = new QProcess; | |
+ pipe_frame.qproc->setReadChannelMode(QProcess::ForwardedChannels); | |
+ | |
+ pipe_frame.qproc->start(rawpipeCmd, QIODevice::WriteOnly/*|QIODevice::Unbuffered*/); | |
+ if(!pipe_frame.qproc->waitForStarted()) { | |
+ qCritical() << "export QProcess start failed"; | |
+ export_error = tr("export QProcess failed"); | |
+ return false; | |
+ } | |
+#else | |
+#error "EXPORT_POPEN must be 1 (popen) or 2 (qprocess)" | |
+#endif | |
+ return true; | |
+ } | |
+#endif | |
+ | |
// find video encoder | |
vcodec = avcodec_find_encoder(static_cast<enum AVCodecID>(params_.video_codec)); | |
if (!vcodec) { | |
@@ -339,6 +383,9 @@ bool ExportThread::SetupAudio() { | |
bool ExportThread::SetupContainer() { | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec == COMPRESSION_RAWPIPE) return true; | |
+#endif | |
// Set up output context (using the filename as the format specification) | |
avformat_alloc_output_context2(&fmt_ctx, nullptr, nullptr, c_filename); | |
@@ -386,17 +433,21 @@ void ExportThread::Export() | |
return; | |
} | |
- // Write the container header based on what's been set up above | |
- ret = avformat_write_header(fmt_ctx, nullptr); | |
- if (ret < 0) { | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec != COMPRESSION_RAWPIPE) { | |
+ // Write the container header based on what's been set up above | |
+ ret = avformat_write_header(fmt_ctx, nullptr); | |
+ if (ret < 0) { | |
- // FFmpeg failed to write the header, so cancel the export and throw an error | |
+ // FFmpeg failed to write the header, so cancel the export and throw an error | |
- qCritical() << "Could not write output file header." << ret; | |
- export_error = tr("could not write output file header (%1)").arg(QString::number(ret)); | |
+ qCritical() << "Could not write output file header." << ret; | |
+ export_error = tr("could not write output file header (%1)").arg(QString::number(ret)); | |
- return; | |
+ return; | |
+ } | |
} | |
+#endif | |
// Count audio samples in file (used for calculating PTS) | |
long file_audio_samples = 0; | |
@@ -431,7 +482,15 @@ void ExportThread::Export() | |
if (params_.video_enabled) { | |
do { | |
// TODO optimize by rendering the next frame while encoding the last | |
- renderer->start_render(nullptr, olive::ActiveSequence.get(), 1, nullptr, video_frame->data[0], video_frame->linesize[0]/4, 0, true); | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec != COMPRESSION_RAWPIPE) { | |
+#endif | |
+ renderer->start_render(nullptr, olive::ActiveSequence.get(), 1, nullptr, video_frame->data[0], video_frame->linesize[0]/4, 0, true); | |
+#ifdef EXPORT_POPEN | |
+ } else { | |
+ renderer->start_render(nullptr, olive::ActiveSequence.get(), 1, nullptr, (GLvoid *)pipe_frame.data, pipe_frame.width, 0, true); | |
+ } | |
+#endif | |
// Wait for RenderThread to return | |
waitCond.wait(&mutex); | |
@@ -456,32 +515,66 @@ void ExportThread::Export() | |
// OpenGL buffer to | |
if (params_.video_enabled) { | |
- // | |
- // - I'm not sure why, but we have to alloc/free sws_frame every frame, or it breaks GIF exporting. | |
- // - (i.e. GIFs get stuck on the first frame) | |
- // - The same problem/solution can be seen here: https://stackoverflow.com/a/38997739 | |
- // - Perhaps this is the intended way to use swscale, but it seems inefficient. | |
- // - Anyway, here we are. | |
- // | |
- | |
- // Construct destination pixel format frame | |
- sws_frame = av_frame_alloc(); | |
- sws_frame->format = vcodec_ctx->pix_fmt; | |
- sws_frame->width = params_.video_width; | |
- sws_frame->height = params_.video_height; | |
- av_frame_get_buffer(sws_frame, 0); | |
- | |
- // Convert raw RGBA buffer to format expected by the encoder | |
- sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize); | |
- sws_frame->pts = qRound(timecode_secs/av_q2d(vcodec_ctx->time_base)); | |
- | |
- // Send frame to encoder | |
- if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) { | |
- return; | |
- } | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec == COMPRESSION_RAWPIPE) { | |
+ unsigned long towrite = pipe_frame.width * 4 * pipe_frame.height, written = 0, partial; | |
+ char *p = (char *)pipe_frame.data; | |
+#if EXPORT_POPEN == 1 | |
+ while (written < towrite) { | |
+ clearerr(pipe_frame.file); | |
+ partial = fwrite(p + written, 1, towrite - written, pipe_frame.file); | |
+ if (ferror(pipe_frame.file)) { | |
+ qCritical() << "Error: writing to pipe"; | |
+ return; | |
+ } | |
+ written += partial; | |
+ } | |
+#elif EXPORT_POPEN == 2 | |
+ while(written < towrite) { | |
+ partial = pipe_frame.qproc->write(p + written, towrite - written); | |
+ if (pipe_frame.qproc->error() == QProcess::WriteError) { | |
+ qCritical() << "Error: qprocess write error"; | |
+ return; | |
+ } | |
+ written += partial; | |
+ while(pipe_frame.qproc->bytesToWrite() > 0) { | |
+ pipe_frame.qproc->waitForBytesWritten(-1); | |
+ } | |
+ } | |
+#else | |
+#error "EXPORT_POPEN must be 1 (popen) or 2 (qprocess)" | |
+#endif | |
+ } else { | |
+#endif | |
+ // | |
+ // - I'm not sure why, but we have to alloc/free sws_frame every frame, or it breaks GIF exporting. | |
+ // - (i.e. GIFs get stuck on the first frame) | |
+ // - The same problem/solution can be seen here: https://stackoverflow.com/a/38997739 | |
+ // - Perhaps this is the intended way to use swscale, but it seems inefficient. | |
+ // - Anyway, here we are. | |
+ // | |
+ | |
+ // Construct destination pixel format frame | |
+ sws_frame = av_frame_alloc(); | |
+ sws_frame->format = vcodec_ctx->pix_fmt; | |
+ sws_frame->width = params_.video_width; | |
+ sws_frame->height = params_.video_height; | |
+ av_frame_get_buffer(sws_frame, 0); | |
+ | |
+ // Convert raw RGBA buffer to format expected by the encoder | |
+ sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize); | |
+ sws_frame->pts = qRound(timecode_secs/av_q2d(vcodec_ctx->time_base)); | |
+ | |
+ // Send frame to encoder | |
+ if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) { | |
+ return; | |
+ } | |
- av_frame_free(&sws_frame); | |
- sws_frame = nullptr; | |
+ av_frame_free(&sws_frame); | |
+ sws_frame = nullptr; | |
+#ifdef EXPORT_POPEN | |
+ } | |
+#endif | |
} | |
// If we're exporting audio, copy audio from the buffer into an AVFrame for encoding | |
@@ -585,73 +678,98 @@ void ExportThread::Export() | |
return; | |
} | |
- // Flush remaining packets out of video and audio encoders by sending a null frame | |
- if (params_.video_enabled) { | |
- Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream); | |
- } | |
- if (params_.audio_enabled) { | |
- Encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream); | |
- } | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec != COMPRESSION_RAWPIPE) { | |
+#endif | |
+ // Flush remaining packets out of video and audio encoders by sending a null frame | |
+ if (params_.video_enabled) { | |
+ Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream); | |
+ } | |
+ if (params_.audio_enabled) { | |
+ Encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream); | |
+ } | |
- // Write container trailer | |
- ret = av_write_trailer(fmt_ctx); | |
- if (ret < 0) { | |
- qCritical() << "Could not write output file trailer." << ret; | |
- export_error = tr("could not write output file trailer (%1)").arg(QString::number(ret)); | |
- return; | |
+ // Write container trailer | |
+ ret = av_write_trailer(fmt_ctx); | |
+ if (ret < 0) { | |
+ qCritical() << "Could not write output file trailer." << ret; | |
+ export_error = tr("could not write output file trailer (%1)").arg(QString::number(ret)); | |
+ return; | |
+ } | |
+#ifdef EXPORT_POPEN | |
} | |
+#endif | |
emit ProgressChanged(100, 0); | |
} | |
void ExportThread::Cleanup() | |
{ | |
- if (fmt_ctx != nullptr) { | |
- avio_closep(&fmt_ctx->pb); | |
- avformat_free_context(fmt_ctx); | |
- } | |
+#ifdef EXPORT_POPEN | |
+ if (params_.video_codec != COMPRESSION_RAWPIPE) { | |
+#endif | |
+ if (fmt_ctx != nullptr) { | |
+ avio_closep(&fmt_ctx->pb); | |
+ avformat_free_context(fmt_ctx); | |
+ } | |
- if (acodec_ctx != nullptr) { | |
- avcodec_close(acodec_ctx); | |
- avcodec_free_context(&acodec_ctx); | |
- } | |
+ if (acodec_ctx != nullptr) { | |
+ avcodec_close(acodec_ctx); | |
+ avcodec_free_context(&acodec_ctx); | |
+ } | |
- if (audio_frame != nullptr) { | |
- av_frame_free(&audio_frame); | |
- } | |
+ if (audio_frame != nullptr) { | |
+ av_frame_free(&audio_frame); | |
+ } | |
- if (apkt_alloc) { | |
- av_packet_unref(&audio_pkt); | |
- } | |
+ if (apkt_alloc) { | |
+ av_packet_unref(&audio_pkt); | |
+ } | |
- if (vcodec_ctx != nullptr) { | |
- avcodec_close(vcodec_ctx); | |
- avcodec_free_context(&vcodec_ctx); | |
- } | |
+ if (vcodec_ctx != nullptr) { | |
+ avcodec_close(vcodec_ctx); | |
+ avcodec_free_context(&vcodec_ctx); | |
+ } | |
- if (video_frame != nullptr) { | |
- av_frame_free(&video_frame); | |
- } | |
+ if (video_frame != nullptr) { | |
+ av_frame_free(&video_frame); | |
+ } | |
- if (vpkt_alloc) { | |
- av_packet_unref(&video_pkt); | |
- } | |
+ if (vpkt_alloc) { | |
+ av_packet_unref(&video_pkt); | |
+ } | |
- if (sws_ctx != nullptr) { | |
- sws_freeContext(sws_ctx); | |
- } | |
+ if (sws_ctx != nullptr) { | |
+ sws_freeContext(sws_ctx); | |
+ } | |
- if (swr_ctx != nullptr) { | |
- swr_free(&swr_ctx); | |
- } | |
+ if (swr_ctx != nullptr) { | |
+ swr_free(&swr_ctx); | |
+ } | |
- if (swr_frame != nullptr) { | |
- av_frame_free(&swr_frame); | |
- } | |
+ if (swr_frame != nullptr) { | |
+ av_frame_free(&swr_frame); | |
+ } | |
- if (sws_frame != nullptr) { | |
- av_frame_free(&sws_frame); | |
- } | |
+ if (sws_frame != nullptr) { | |
+ av_frame_free(&sws_frame); | |
+ } | |
+#ifdef EXPORT_POPEN | |
+ } else { | |
+#if EXPORT_POPEN == 1 | |
+ pclose(pipe_frame.file); | |
+#elif EXPORT_POPEN == 2 | |
+ pipe_frame.qproc->waitForBytesWritten(-1); | |
+ pipe_frame.qproc->closeWriteChannel(); | |
+ pipe_frame.qproc->waitForFinished(); | |
+ //pipe_frame.qproc->terminate(); | |
+ pipe_frame.qproc->close(); | |
+#else | |
+#error "EXPORT_POPEN must be 1 (popen) or 2 (qprocess)" | |
+#endif | |
+ delete [] (char *)pipe_frame.data; | |
+ } | |
+#endif | |
delete [] c_filename; | |
} | |
diff --git a/rendering/exportthread.h b/rendering/exportthread.h | |
index 32822172..21d43d9e 100644 | |
--- a/rendering/exportthread.h | |
+++ b/rendering/exportthread.h | |
@@ -21,10 +21,15 @@ | |
#ifndef EXPORTTHREAD_H | |
#define EXPORTTHREAD_H | |
+#define EXPORT_POPEN 2 | |
+ | |
#include <QThread> | |
#include <QOffscreenSurface> | |
#include <QMutex> | |
#include <QWaitCondition> | |
+#if defined(EXPORT_POPEN) && EXPORT_POPEN == 2 | |
+#include <QProcess> | |
+#endif | |
struct AVFormatContext; | |
struct AVCodecContext; | |
@@ -37,12 +42,19 @@ struct SwrContext; | |
extern "C" { | |
#include <libavcodec/avcodec.h> | |
+#if defined(EXPORT_POPEN) && EXPORT_POPEN == 1 | |
+#include <stdio.h> | |
+#endif | |
} | |
+ | |
#define COMPRESSION_TYPE_CBR 0 | |
#define COMPRESSION_TYPE_CFR 1 | |
#define COMPRESSION_TYPE_TARGETSIZE 2 | |
#define COMPRESSION_TYPE_TARGETBR 3 | |
+#ifdef EXPORT_POPEN | |
+#define COMPRESSION_RAWPIPE 0x30000 | |
+#endif | |
// structs that store parameters passed from the export dialogs to this thread | |
@@ -69,6 +81,23 @@ struct VideoCodecParams { | |
int threads; | |
}; | |
+#ifdef EXPORT_POPEN | |
+struct PipeFrame { | |
+ int format; | |
+ unsigned short width; | |
+ unsigned short height; | |
+ unsigned long *data; // unsigned | |
+#if EXPORT_POPEN == 1 | |
+ FILE *file; | |
+#elif EXPORT_POPEN == 2 | |
+ QProcess *qproc; | |
+#else | |
+#error "EXPORT_POPEN must be 1 (popen) or 2 (qprocess)" | |
+#endif | |
+}; | |
+#endif | |
+ | |
+ | |
class ExportThread : public QThread { | |
Q_OBJECT | |
public: | |
@@ -118,6 +147,10 @@ private: | |
bool vpkt_alloc; | |
bool apkt_alloc; | |
+#ifdef EXPORT_POPEN | |
+ struct PipeFrame pipe_frame; | |
+#endif | |
+ | |
int aframe_bytes; | |
int ret; | |
char* c_filename; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment