Krita Source Code Documentation
Loading...
Searching...
No Matches
KisLibavMediaEncoderRunnable.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 */
5
6#include <QBuffer>
7#include <QFile>
8#include <QImage>
9#include <QVBoxLayout>
10#include <memory>
11
12#include <klocalizedstring.h>
13
14#include <kis_debug.h>
15
16extern "C" {
17#include <libavcodec/avcodec.h>
18#include <libavfilter/avfilter.h>
19#include <libavfilter/buffersink.h>
20#include <libavfilter/buffersrc.h>
21#include <libavformat/avformat.h>
22#include <libavutil/error.h>
23#include <libavutil/opt.h>
24#include <libavutil/pixfmt.h>
25#include <libswscale/swscale.h>
26}
27
29
30namespace KisLibav
31{
32// Same as IO_BUFFER_SIZE in ffmpeg's avio.c.
33static constexpr int BUFFER_SIZE = 32768;
34
35// GIF palette sizes, always 16x16 images in BGRA format.
36static constexpr int GIF_PALETTE_DIMENSION = 16;
37static constexpr int GIF_PALETTE_BYTES = 1024;
38
39// FFMPEG API breakage. They added constness to a callback and changed
40// their mind on which macro you use to check for it or something.
41#if defined(ff_const59)
42using WriteAvioPacketBuf = ff_const59 uint8_t *;
43#elif FF_API_AVIO_WRITE_NONCONST
44using WriteAvioPacketBuf = uint8_t *;
45#else
46using WriteAvioPacketBuf = const uint8_t *;
47#endif
48} // namespace KisLibav
49
51{
52public:
58
59 int formatId() const
60 {
61 return m_formatId;
62 }
63
64 Type type() const override
65 {
67 }
68
69 QString key() const override
70 {
72 }
73
74 QString title() const override
75 {
76 return i18n("LibAV: %1", titleForFormatId(m_formatId));
77 }
78
79 QString extension() const override
80 {
82 }
83
84 bool supportsAudio() const override
85 {
86 return false;
87 }
88
89 QString formatName() const
90 {
91 switch (m_formatId) {
93 return QStringLiteral("rawvideo");
94 case FORMAT_GIF:
95 return QStringLiteral("gif");
96 case FORMAT_WEBP:
97 return QStringLiteral("webp");
98 case FORMAT_APNG:
99 return QStringLiteral("apng");
100 }
101 return QString();
102 }
103
104 AVCodecID codecId() const
105 {
107 }
108
109 static AVCodecID codecIdForFormatId(int formatId)
110 {
111 switch (formatId) {
113 return AV_CODEC_ID_RAWVIDEO;
114 case FORMAT_GIF:
115 return AV_CODEC_ID_GIF;
116 case FORMAT_WEBP:
117 return AV_CODEC_ID_WEBP;
118 case FORMAT_APNG:
119 return AV_CODEC_ID_APNG;
120 }
121 return AV_CODEC_ID_NONE;
122 }
123
124 AVPixelFormat pixelFormat(bool frame) const
125 {
126 switch (m_formatId) {
128 case FORMAT_WEBP:
129 return AV_PIX_FMT_BGRA;
130 case FORMAT_GIF:
131 return frame ? AV_PIX_FMT_BGRA : AV_PIX_FMT_PAL8;
132 case FORMAT_APNG:
133 return AV_PIX_FMT_RGBA;
134 }
135 return AV_PIX_FMT_NONE;
136 }
137
138 void setCodecParams(AVCodecContext *codecContext) const
139 {
140 switch (m_formatId) {
142 case FORMAT_GIF:
143 case FORMAT_APNG:
144 break;
145 case FORMAT_WEBP:
146 setOption(codecContext->priv_data, "lossless", "1");
147 setOption(codecContext->priv_data, "preset", "drawing");
148 setOption(codecContext->priv_data, "quality", "100");
149 break;
150 }
151 }
152
153 void setOutputParams(AVFormatContext *formatContext) const
154 {
155 switch (m_formatId) {
157 case FORMAT_GIF:
158 break;
159 case FORMAT_WEBP:
160 setOption(formatContext->priv_data, "loop", "0");
161 break;
162 case FORMAT_APNG:
163 setOption(formatContext->priv_data, "plays", "0");
164 break;
165 }
166 }
167
168 QByteArray filterDesc() const
169 {
170 switch (m_formatId) {
171 case FORMAT_WEBP:
172 case FORMAT_APNG:
173 break;
175 return QByteArrayLiteral("palettegen=stats_mode=full");
176 case FORMAT_GIF:
177 return QByteArrayLiteral("[in][palette]paletteuse[out]");
178 }
179 return QByteArray();
180 }
181
183 {
184 switch (m_formatId) {
185 case FORMAT_GIF:
186 return {AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE};
187 default:
188 return {AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE};
189 }
190 }
191
192 QWidget *createPreferencesWidget(const QVariantMap &preferences) const override
193 {
194 Q_UNUSED(preferences);
195 return nullptr;
196 }
197
198 void resetPreferencesWidget(QWidget *widget) const override
199 {
200 Q_UNUSED(widget);
201 }
202
203 QVariantMap getPreferencesFromWidget(QWidget *widget) const override
204 {
205 Q_UNUSED(widget);
206 return QVariantMap();
207 }
208
209private:
210 static QString keyForFormatId(int formatId)
211 {
212 switch (formatId) {
213 case FORMAT_GIF:
214 return QStringLiteral("libav:gif");
215 case FORMAT_WEBP:
216 return QStringLiteral("libav:webp");
217 case FORMAT_APNG:
218 return QStringLiteral("libav:apng");
219 }
220 return QString();
221 }
222
223 static QString titleForFormatId(int formatId)
224 {
225 switch (formatId) {
226 case FORMAT_GIF:
227 return QStringLiteral("GIF");
228 case FORMAT_WEBP:
229 return QStringLiteral("WEBP");
230 case FORMAT_APNG:
231 return QStringLiteral("APNG");
232 }
233 return QString();
234 }
235
236 static QString extensionForFormatId(int formatId)
237 {
238 switch (formatId) {
239 case FORMAT_GIF:
240 return QStringLiteral("gif");
241 case FORMAT_WEBP:
242 return QStringLiteral("webp");
243 case FORMAT_APNG:
244 return QStringLiteral("apng");
245 }
246 return QString();
247 }
248
249 static void setOption(void *obj, const char *name, const char *value)
250 {
251 int result = av_opt_set(obj, name, value, AV_OPT_SEARCH_CHILDREN);
252 if (result != 0) {
253 warnFile.nospace() << "Error setting option " << name << " to " << value << ": " << av_err2str(result)
254 << " (" << result << ")";
255 }
256 }
257
259};
260
262{
263public:
264 Context(KisLibavMediaEncoderRunnable *runnable, QString &outErrorMessage)
265 : KisLibavEncoderContext(&outErrorMessage)
266 , m_runnable(runnable)
267 {
268 }
269
270 ~Context() override
271 {
272 if (m_formatContext && m_formatContext->pb) {
273 avio_context_free(&m_formatContext->pb);
274 }
275 avfilter_graph_free(&m_graphContext);
276 avfilter_inout_free(&m_filterInputs);
277 avfilter_inout_free(&m_filterOutputs);
278 avfilter_inout_free(&m_filterPaletteOutputs);
279 av_packet_free(&m_packet);
280 av_frame_free(&m_frame);
281 av_frame_free(&m_filteredFrame);
282 av_frame_free(&m_paletteFrame);
283 avcodec_parameters_free(&m_codecParameters);
284 avformat_free_context(m_formatContext);
285 avcodec_free_context(&m_codecContext);
286 }
287
289 const Format &format,
290 QByteArray &paletteData,
291 bool writePalette)
292 {
293 if (isCancelled()) {
295 }
296
297 QString formatName = format.formatName();
298 if (formatName.isEmpty()) {
299 setInternalErrorMessage(QStringLiteral("Unknown format name"));
301 }
302
303 AVCodecID codecId = format.codecId();
304 if (codecId == AV_CODEC_ID_NONE) {
305 setInternalErrorMessage(QStringLiteral("Unknown codec"));
307 }
308
309 const AVOutputFormat *outputFormat = av_guess_format(formatName.toUtf8().constData(), nullptr, nullptr);
310 if (!outputFormat) {
311 setInternalErrorMessage(QStringLiteral("av_guess_format failed for %1").arg(formatName));
313 }
314
315 const AVCodec *codec = avcodec_find_encoder(codecId);
316 if (!codec) {
317 setInternalErrorMessage(QStringLiteral("avcodec_find_encoder failed for %1").arg(int(codecId)));
319 }
320
321 m_codecContext = avcodec_alloc_context3(codec);
322 if (!m_codecContext) {
323 setInternalErrorMessage(QStringLiteral("avcodec_alloc_context3 failed for %1").arg(int(codecId)));
325 }
326
327 int outputWidth = settings.outputSize.width();
328 int outputHeight = settings.outputSize.height();
329 double framerate = settings.outputFps;
330
331 m_codecContext->width = outputWidth;
332 m_codecContext->height = outputHeight;
333 m_codecContext->pix_fmt = format.pixelFormat(false);
334 m_codecContext->framerate = av_d2q(framerate, 1000000000);
335 m_codecContext->time_base = av_inv_q(m_codecContext->framerate);
337
338 int err = avformat_alloc_output_context2(&m_formatContext, outputFormat, nullptr, nullptr);
339 if (err != 0) {
340 setInternalErrorMessageAv(QStringLiteral("avformat_alloc_output_context2 failed"), err);
342 }
343
345
346 AVStream *stream = avformat_new_stream(m_formatContext, nullptr);
347 if (!stream) {
348 setInternalErrorMessage(QStringLiteral("avformat_new_stream failed"));
350 }
351
352 stream->avg_frame_rate = m_codecContext->framerate;
353 stream->time_base = m_codecContext->time_base;
354 err = avcodec_open2(m_codecContext, codec, nullptr);
355 if (err != 0) {
356 setInternalErrorMessageAv(QStringLiteral("avcodec_open2 failed"), err);
358 }
359
360 m_codecParameters = avcodec_parameters_alloc();
361 if (!m_codecParameters) {
362 setInternalErrorMessage(QStringLiteral("avcodec_parameters_alloc failed"));
364 }
365
366 err = avcodec_parameters_from_context(m_codecParameters, m_codecContext);
367 if (err != 0) {
368 setInternalErrorMessageAv(QStringLiteral("avcodec_parameters_from_context failed"), err);
370 }
371
372 err = avcodec_parameters_copy(stream->codecpar, m_codecParameters);
373 avcodec_parameters_free(&m_codecParameters);
374 if (err != 0) {
375 setInternalErrorMessageAv(QStringLiteral("avcodec_parameters_copy failed"), err);
377 }
378
379 if (m_formatContext->oformat->flags & AVFMT_GLOBALHEADER) {
380 m_formatContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
381 }
382
383 QByteArray filterDesc = format.filterDesc();
384 if (!filterDesc.isEmpty()) {
385 const AVFilter *buffersrcFilter = avfilter_get_by_name("buffer");
386 if (!buffersrcFilter) {
387 setInternalErrorMessage(QStringLiteral("buffer filter not found"));
389 }
390
391 const AVFilter *buffersinkFilter = avfilter_get_by_name("buffersink");
392 if (!buffersinkFilter) {
393 setInternalErrorMessage(QStringLiteral("buffersink filter not found"));
395 }
396
397 m_filteredFrame = av_frame_alloc();
398 if (!m_filteredFrame) {
399 setInternalErrorMessage(QStringLiteral("filter av_frame_alloc failed"));
401 }
402
403 if (!paletteData.isEmpty()) {
404 m_filterPaletteOutputs = avfilter_inout_alloc();
406 setInternalErrorMessage(QStringLiteral("palette outputs avfilter_inout_alloc failed"));
408 }
409 }
410
411 m_filterOutputs = avfilter_inout_alloc();
412 if (!m_filterOutputs) {
413 setInternalErrorMessage(QStringLiteral("filter outputs avfilter_inout_alloc failed"));
415 }
416
417 m_filterInputs = avfilter_inout_alloc();
418 if (!m_filterInputs) {
419 setInternalErrorMessage(QStringLiteral("filter inputs avfilter_inout_alloc failed"));
421 }
422
423 m_graphContext = avfilter_graph_alloc();
424 if (!m_graphContext) {
425 setInternalErrorMessage(QStringLiteral("avfilter_graph_alloc failed"));
427 }
428
429 {
430 QByteArray buffersrcArgs =
431 QStringLiteral("video_size=%1x%2:pix_fmt=%3:time_base=%4/%5:pixel_aspect=%6/%7")
432 .arg(outputWidth)
433 .arg(outputHeight)
434 .arg(int(format.pixelFormat(true)))
435 .arg(stream->time_base.num)
436 .arg(stream->time_base.den)
437 .arg(m_codecContext->sample_aspect_ratio.num)
438 .arg(m_codecContext->sample_aspect_ratio.den)
439 .toUtf8();
440 err = avfilter_graph_create_filter(&m_buffersrcContext,
441 buffersrcFilter,
442 "in",
443 buffersrcArgs.constData(),
444 nullptr,
446 if (err < 0) {
447 setInternalErrorMessageAv(QStringLiteral("buffersrc avfilter_graph_create_filter failed"), err);
449 }
450 }
451
452 if (!paletteData.isEmpty()) {
455 QByteArray palettesrcArgs =
456 QStringLiteral("video_size=%1x%1:pix_fmt=%2:time_base=1/24:pixel_aspect=0/1")
458 .arg(int(AV_PIX_FMT_BGRA))
459 .toUtf8();
460 err = avfilter_graph_create_filter(&m_palettesrcContext,
461 buffersrcFilter,
462 "palette",
463 palettesrcArgs.constData(),
464 nullptr,
466 if (err < 0) {
467 setInternalErrorMessageAv(QStringLiteral("palettesrc avfilter_graph_create_filter failed"), err);
469 }
470 }
471
472 m_buffersinkContext = avfilter_graph_alloc_filter(m_graphContext, buffersinkFilter, "out");
473 if (!m_buffersinkContext) {
474 setInternalErrorMessageAv(QStringLiteral("buffersink avfilter_graph_alloc_filter failed"), err);
476 }
477
479#if LIBAVUTIL_VERSION_MAJOR >= 60
480 // Option int lists and "pix_fmt" got deprecated and replaced with array
481 // options and "pixel_formats".
482 unsigned int pixFmtsCount = 0;
483 for (AVPixelFormat pixFmt : pixFmts) {
484 if (pixFmt == AV_PIX_FMT_NONE) {
485 break;
486 } else {
487 ++pixFmtsCount;
488 }
489 }
490 err = av_opt_set_array(m_buffersinkContext,
491 "pixel_formats",
492 AV_OPT_SEARCH_CHILDREN | AV_OPT_ARRAY_REPLACE,
493 0,
494 pixFmtsCount,
495 AV_OPT_TYPE_PIXEL_FMT,
496 pixFmts.constData());
497#else
498 err = av_opt_set_int_list(m_buffersinkContext,
499 "pix_fmts",
500 pixFmts.constData(),
501 AV_PIX_FMT_NONE,
502 AV_OPT_SEARCH_CHILDREN);
503#endif
504 if (err < 0) {
505 setInternalErrorMessageAv(QStringLiteral("buffersink av_opt_set failed"), err);
507 }
508
509 err = avfilter_init_str(m_buffersinkContext, nullptr);
510 if (err < 0) {
511 setInternalErrorMessageAv(QStringLiteral("buffersink avfilter_init_str failed"), err);
513 }
514
516 m_filterPaletteOutputs->name = av_strdup("palette");
518 m_filterPaletteOutputs->pad_idx = 0;
519 m_filterPaletteOutputs->next = nullptr;
520 }
521
522 m_filterOutputs->name = av_strdup("in");
524 m_filterOutputs->pad_idx = 0;
526 m_filterPaletteOutputs = nullptr;
527
528 m_filterInputs->name = av_strdup("out");
530 m_filterInputs->pad_idx = 0;
531 m_filterInputs->next = nullptr;
532
533 err = avfilter_graph_parse_ptr(m_graphContext,
534 filterDesc.constData(),
537 nullptr);
538 if (err < 0) {
539 setInternalErrorMessageAv(QStringLiteral("avfilter_graph_parse_ptr failed"), err);
541 }
542
543 err = avfilter_graph_config(m_graphContext, nullptr);
544 if (err < 0) {
546 QStringLiteral("avfilter_graph_config failed").arg(QString::fromUtf8(filterDesc)),
547 err);
549 }
550
551 avfilter_inout_free(&m_filterOutputs);
552 avfilter_inout_free(&m_filterInputs);
553 }
554
555 if (isCancelled()) {
557 }
558
559 std::unique_ptr<QIODevice> output;
560 if (writePalette) {
562 output = std::make_unique<QBuffer>(&paletteData);
563 if (!output->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
565 QStringLiteral("failed to open palette output buffer: %1").arg(output->errorString()));
567 }
568 } else {
569 output = std::make_unique<QFile>(settings.outputFile);
570 if (!output->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
571 setInternalErrorMessage(QStringLiteral("failed to open output file '%1': %2")
572 .arg(settings.outputFile)
573 .arg(output->errorString()));
575 }
576 }
577
578 if (isCancelled()) {
580 }
581
582 QByteArray buffer;
583 buffer.resize(KisLibav::BUFFER_SIZE);
584 m_formatContext->pb = avio_alloc_context(reinterpret_cast<unsigned char *>(buffer.data()),
585 buffer.size(),
586 AVIO_FLAG_WRITE,
587 output.get(),
588 nullptr,
591 if (!m_formatContext->pb) {
592 setInternalErrorMessage(QStringLiteral("avio_alloc_context failed"));
594 }
595
596 m_formatContext->flags |= AVFMT_FLAG_CUSTOM_IO;
597 m_formatContext->pb->seekable = AVIO_SEEKABLE_NORMAL;
598
599 err = avformat_write_header(m_formatContext, nullptr);
600 if (err != 0) {
601 setInternalErrorMessageAv(QStringLiteral("avformat_write_header failed"), err);
603 }
604
605 m_frame = av_frame_alloc();
606 if (!m_frame) {
607 setInternalErrorMessage(QStringLiteral("av_frame_alloc failed"));
609 }
610
611 m_frame->width = outputWidth;
612 m_frame->height = outputHeight;
613 m_frame->format = format.pixelFormat(true);
614
615 err = av_frame_get_buffer(m_frame, 0);
616 if (err != 0) {
617 setInternalErrorMessageAv(QStringLiteral("av_frame_get_buffer failed"), err);
619 }
620
621 m_packet = av_packet_alloc();
622 if (!m_packet) {
623 setInternalErrorMessage(QStringLiteral("av_packet_alloc failed"));
625 }
626
627 if (isCancelled()) {
629 }
630
632 m_paletteFrame = av_frame_alloc();
633 if (!m_paletteFrame) {
634 setInternalErrorMessage(QStringLiteral("palette av_frame_alloc failed"));
636 }
637
640 m_paletteFrame->format = AV_PIX_FMT_BGRA;
641
642 err = av_frame_get_buffer(m_paletteFrame, 0);
643 if (err != 0) {
644 setInternalErrorMessageAv(QStringLiteral("palette av_frame_get_buffer failed"), err);
646 }
647
648 err = av_frame_make_writable(m_paletteFrame);
649 if (err != 0) {
650 setInternalErrorMessageAv(QStringLiteral("palette av_frame_make_writable failed"), err);
652 }
653
656 memcpy(m_paletteFrame->data[0], paletteData.constData(), KisLibav::GIF_PALETTE_BYTES);
657 err = av_buffersrc_add_frame_flags(m_palettesrcContext, m_paletteFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
658 if (err < 0) {
659 setInternalErrorMessageAv(QStringLiteral("palette feed av_buffersrc_add_frame_flags failed"), err);
661 }
662
663 err = av_buffersrc_add_frame_flags(m_palettesrcContext, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
664 if (err < 0) {
665 setInternalErrorMessageAv(QStringLiteral("palette flush v_buffersrc_add_frame_flags failed"), err);
667 }
668 }
669
670 m_frame->pts = 0;
671 Frame inputFrame;
672 int swsFlags = getSwsFlags(settings.scaleFilter);
673 while (m_runnable->nextFrame(inputFrame)) {
674 if (isCancelled()) {
676 }
677
678 // Grab the next frame from disk.
679 QImage inputImage;
680 AVPixelFormat inputPixelFormat;
681 if (!inputFrame.readImage(inputImage) || !convertFrame(inputImage, inputPixelFormat)) {
682 continue; // Keep going, some frames may be corrupted.
683 }
684
685 int inputWidth = inputImage.width();
686 int inputHeight = inputImage.height();
687 if (inputWidth <= 0 || inputHeight <= 0 || inputImage.isNull()) {
688 setInternalErrorMessage(QStringLiteral("null frame image"));
690 }
691
692 int instances = inputFrame.instances();
693 if (instances < 1) {
694 setInternalErrorMessage(QStringLiteral("frame has %1 instances < 1").arg(instances));
696 }
697
698 err = av_frame_make_writable(m_frame);
699 if (err != 0) {
700 setInternalErrorMessageAv(QStringLiteral("frame av_frame_make_writable failed"), err);
702 }
703
704 SwsContext *swsContext = getSwsContextFor(inputWidth,
705 inputHeight,
706 inputPixelFormat,
707 outputWidth,
708 outputHeight,
709 AVPixelFormat(m_frame->format),
710 swsFlags);
711 if (!swsContext) {
712 setInternalErrorMessage(QStringLiteral("sws_getCachedContext failed"));
714 }
715
716 const uint8_t *data = reinterpret_cast<const uint8_t *>(inputImage.constBits());
717 const int stride = inputImage.bytesPerLine();
718 sws_scale(swsContext, &data, &stride, 0, inputHeight, m_frame->data, m_frame->linesize);
719
720 for (int i = 0; i < instances; ++i) {
721 if (!filterFrame(stream, m_frame)) {
723 }
724 ++m_frame->pts;
725 }
726 }
727
728 av_frame_free(&m_frame);
729
730 if (isCancelled()) {
732 }
733
734 if (!filterFrame(stream, nullptr)) {
736 }
737
738 av_packet_free(&m_packet);
739
740 if (isCancelled()) {
742 }
743
744 err = av_write_trailer(m_formatContext);
745 if (err != 0) {
746 setInternalErrorMessageAv(QStringLiteral("av_write_trailer failed"), err);
748 }
749
750 if (isCancelled()) {
752 }
753
754 avio_flush(m_formatContext->pb);
755 avio_context_free(&m_formatContext->pb);
756
757 // QIODevice doesn't have a flush function, so we gotta go via a cast.
758 QFile *outputFile = qobject_cast<QFile *>(output.get());
759 if (outputFile && !outputFile->flush()) {
760 setInternalErrorMessage(QStringLiteral("failed to flush output file '%1': %2")
761 .arg(settings.outputFile)
762 .arg(outputFile->errorString()));
764 }
765 output->close();
766
768 }
769
770private:
771 bool isCancelled() const
772 {
773 return m_runnable->isCancelled();
774 }
775
776 bool filterFrame(AVStream *stream, AVFrame *frame)
777 {
778 if (m_buffersrcContext) {
781 int err = av_buffersrc_add_frame_flags(m_buffersrcContext, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
782 if (err < 0) {
783 setInternalErrorMessageAv(QStringLiteral("feed av_buffersrc_add_frame_flags failed"), err);
784 return false;
785 }
786
787 while (true) {
788 err = av_buffersink_get_frame(m_buffersinkContext, m_filteredFrame);
789 if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) {
790 return true;
791 } else if (err != 0) {
792 setInternalErrorMessageAv(QStringLiteral("av_buffersink_get_frame failed"), err);
793 return false;
794 }
795
796 bool ok = handleFrame(stream, m_filteredFrame);
797 av_frame_unref(m_filteredFrame);
798 return ok;
799 }
800 } else {
803 return handleFrame(stream, frame);
804 }
805 }
806
807 bool handleFrame(AVStream *stream, AVFrame *frame)
808 {
809 int err = avcodec_send_frame(m_codecContext, frame);
810 if (err != 0) {
811 setInternalErrorMessageAv(QStringLiteral("avcodec_send_frame failed"), err);
812 return false;
813 }
814
815 while (true) {
816 err = avcodec_receive_packet(m_codecContext, m_packet);
817 if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) {
818 return true;
819 } else if (err != 0) {
820 setInternalErrorMessageAv(QStringLiteral("avcodec_receive_packet failed"), err);
821 return false;
822 }
823
824 av_packet_rescale_ts(m_packet, m_codecContext->time_base, stream->time_base);
825 m_packet->stream_index = stream->index;
826
827 err = av_interleaved_write_frame(m_formatContext, m_packet);
828 if (err != 0) {
829 setInternalErrorMessageAv(QStringLiteral("av_interleaved_write_frame failed"), err);
830 return false;
831 }
832 }
833 }
834
835 void setInternalErrorMessageAv(const QString &detail, int err)
836 {
838 QStringLiteral("%1: %2 (%3)").arg(detail).arg(QString::fromUtf8(av_err2str(err))).arg(err));
839 }
840
841 static int writeAvioPacket(void *opaque, KisLibav::WriteAvioPacketBuf buf, int bufSize)
842 {
843 QIODevice *output = static_cast<QIODevice *>(opaque);
844 if (bufSize > 0) {
845 qint64 toWrite = bufSize;
846 qint64 written = output->write(reinterpret_cast<const char *>(buf), toWrite);
847 if (written != toWrite) {
848 return AVERROR(EIO);
849 }
850 }
851 return bufSize;
852 }
853
854 static int64_t seekSetAvio(QIODevice *output, qint64 offset)
855 {
856 if (output->seek(offset)) {
857 return offset;
858 } else {
859 return AVERROR(EIO);
860 }
861 }
862
863 static int64_t seekCurAvio(QIODevice *output, qint64 offset)
864 {
865 qint64 pos = output->pos();
866 if (pos < 0) {
867 return AVERROR(EIO);
868 }
869
870 qint64 targetPos = qMax(qint64(0), pos + offset);
871 if (targetPos != pos && !output->seek(targetPos)) {
872 return AVERROR(EIO);
873 }
874
875 return targetPos;
876 }
877
878 static int64_t seekAvio(void *opaque, int64_t offset, int whence)
879 {
880 QIODevice *output = static_cast<QIODevice *>(opaque);
881 if (whence & AVSEEK_SIZE) {
882 warnFile << "AVSEEK_SIZE unsupported with whence" << whence;
883 return 0;
884 } else {
885 switch (whence & ~AVSEEK_FORCE) {
886 case SEEK_SET:
887 return seekSetAvio(output, offset);
888 case SEEK_CUR:
889 return seekCurAvio(output, offset);
890 default:
891 warnFile << "Unsupported seek whence" << whence;
892 return AVERROR(EINVAL);
893 }
894 }
895 }
896
898 AVCodecContext *m_codecContext = nullptr;
899 AVFormatContext *m_formatContext = nullptr;
900 AVCodecParameters *m_codecParameters = nullptr;
901 AVFilterInOut *m_filterPaletteOutputs = nullptr;
902 AVFilterInOut *m_filterOutputs = nullptr;
903 AVFilterInOut *m_filterInputs = nullptr;
904 AVFilterGraph *m_graphContext = nullptr;
905 AVFilterContext *m_buffersrcContext = nullptr;
906 AVFilterContext *m_palettesrcContext = nullptr;
907 AVFilterContext *m_buffersinkContext = nullptr;
908 AVFrame *m_paletteFrame = nullptr;
909 AVFrame *m_frame = nullptr;
910 AVFrame *m_filteredFrame = nullptr;
911 AVPacket *m_packet = nullptr;
912};
913
915 QObject *parent)
916{
918 return new KisLibavMediaEncoderRunnable(settings, parent);
919 } else {
920 return nullptr;
921 }
922}
923
925{
926 if (avcodec_find_encoder(Format::codecIdForFormatId(FORMAT_GIF_PALETTE))
927 && avcodec_find_encoder(Format::codecIdForFormatId(FORMAT_GIF))) {
928 outSupportedFormats.append(new Format(FORMAT_GIF));
929 }
930}
931
937
939{
940 Format *format = static_cast<Format *>(settings().format);
941 QByteArray paletteData;
942
943 if (format->formatId() == FORMAT_GIF) {
944 // GIF needs to generate a palette, so we must do two rendering runs.
945 // Setting the multiplier here halves the speed of the progress bar.
947
948 Format paletteFormat(FORMAT_GIF_PALETTE);
949 EncodeResult paletteResult =
950 Context(this, outErrorMessage).encode(settings(), paletteFormat, paletteData, true);
951 if (paletteResult != EncodeResult::Completed) {
952 return paletteResult;
953 }
954
956 rewindFrames(); // Ready the frame iterator for another round.
957 }
958
959 return Context(this, outErrorMessage).encode(settings(), *format, paletteData, false);
960}
float value(const T *src, size_t ch)
SwsContext * getSwsContextFor(int inputWidth, int inputHeight, AVPixelFormat inputFormat, int outputWidth, int outputHeight, AVPixelFormat outputFormat, int flags)
void setInternalErrorMessage(const QString &detail)
int getSwsFlags(const QString &scaleFilter) const
bool convertFrame(QImage &inOutImage, AVPixelFormat &outPixelFormat) const
bool filterFrame(AVStream *stream, AVFrame *frame)
static int writeAvioPacket(void *opaque, KisLibav::WriteAvioPacketBuf buf, int bufSize)
bool handleFrame(AVStream *stream, AVFrame *frame)
EncodeResult encode(const KisMediaEncoderWrapperSettings &settings, const Format &format, QByteArray &paletteData, bool writePalette)
static int64_t seekAvio(void *opaque, int64_t offset, int whence)
Context(KisLibavMediaEncoderRunnable *runnable, QString &outErrorMessage)
void setInternalErrorMessageAv(const QString &detail, int err)
static int64_t seekCurAvio(QIODevice *output, qint64 offset)
static int64_t seekSetAvio(QIODevice *output, qint64 offset)
QVector< AVPixelFormat > filterPixelFormats() const
static void setOption(void *obj, const char *name, const char *value)
void resetPreferencesWidget(QWidget *widget) const override
void setCodecParams(AVCodecContext *codecContext) const
void setOutputParams(AVFormatContext *formatContext) const
QWidget * createPreferencesWidget(const QVariantMap &preferences) const override
QVariantMap getPreferencesFromWidget(QWidget *widget) const override
KisLibavMediaEncoderRunnable(const KisMediaEncoderWrapperSettings &settings, QObject *parent)
static KisLibavMediaEncoderRunnable * create(const KisMediaEncoderWrapperSettings &settings, QObject *parent=nullptr)
EncodeResult encode(QString &outErrorMessage) override
static void getSupportedFormats(QVector< KisMediaEncoderFormat * > &outSupportedFormats)
virtual Type type() const =0
bool readImage(QImage &outImage) const
const KisMediaEncoderWrapperSettings settings()
bool nextFrame(Frame &outFrame)
void setOutputFrameNoMultiplier(double outputFrameNoMultiplier)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define warnFile
Definition kis_debug.h:99
static constexpr int BUFFER_SIZE
const uint8_t * WriteAvioPacketBuf
static constexpr int GIF_PALETTE_DIMENSION
static constexpr int GIF_PALETTE_BYTES