292 {
295 }
296
297 QString formatName = format.formatName();
298 if (formatName.isEmpty()) {
301 }
302
303 AVCodecID codecId = format.codecId();
304 if (codecId == AV_CODEC_ID_NONE) {
307 }
308
309 const AVOutputFormat *outputFormat = av_guess_format(formatName.toUtf8().constData(), nullptr, nullptr);
310 if (!outputFormat) {
313 }
314
315 const AVCodec *codec = avcodec_find_encoder(codecId);
316 if (!codec) {
319 }
320
325 }
326
330
337
338 int err = avformat_alloc_output_context2(&
m_formatContext, outputFormat,
nullptr,
nullptr);
339 if (err != 0) {
342 }
343
345
347 if (!stream) {
350 }
351
355 if (err != 0) {
358 }
359
364 }
365
367 if (err != 0) {
370 }
371
374 if (err != 0) {
377 }
378
381 }
382
383 QByteArray filterDesc = format.filterDesc();
384 if (!filterDesc.isEmpty()) {
385 const AVFilter *buffersrcFilter = avfilter_get_by_name("buffer");
386 if (!buffersrcFilter) {
389 }
390
391 const AVFilter *buffersinkFilter = avfilter_get_by_name("buffersink");
392 if (!buffersinkFilter) {
395 }
396
401 }
402
403 if (!paletteData.isEmpty()) {
408 }
409 }
410
415 }
416
421 }
422
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)
439 .toUtf8();
441 buffersrcFilter,
442 "in",
443 buffersrcArgs.constData(),
444 nullptr,
446 if (err < 0) {
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();
461 buffersrcFilter,
462 "palette",
463 palettesrcArgs.constData(),
464 nullptr,
466 if (err < 0) {
469 }
470 }
471
476 }
477
479#if LIBAVUTIL_VERSION_MAJOR >= 60
480
481
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 }
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
499 "pix_fmts",
500 pixFmts.constData(),
501 AV_PIX_FMT_NONE,
502 AV_OPT_SEARCH_CHILDREN);
503#endif
504 if (err < 0) {
507 }
508
510 if (err < 0) {
513 }
514
520 }
521
527
532
534 filterDesc.constData(),
537 nullptr);
538 if (err < 0) {
541 }
542
544 if (err < 0) {
546 QStringLiteral("avfilter_graph_config failed").arg(QString::fromUtf8(filterDesc)),
547 err);
549 }
550
553 }
554
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 {
570 if (!output->open(QIODevice::WriteOnly | QIODevice::Truncate)) {
573 .arg(output->errorString()));
575 }
576 }
577
580 }
581
582 QByteArray buffer;
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,
594 }
595
598
600 if (err != 0) {
603 }
604
609 }
610
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) {
619 }
620
625 }
626
629 }
630
636 }
637
641
643 if (err != 0) {
646 }
647
649 if (err != 0) {
652 }
653
658 if (err < 0) {
661 }
662
663 err = av_buffersrc_add_frame_flags(
m_palettesrcContext,
nullptr, AV_BUFFERSRC_FLAG_KEEP_REF);
664 if (err < 0) {
667 }
668 }
669
671 Frame inputFrame;
676 }
677
678
679 QImage inputImage;
680 AVPixelFormat inputPixelFormat;
681 if (!inputFrame.readImage(inputImage) || !
convertFrame(inputImage, inputPixelFormat)) {
682 continue;
683 }
684
685 int inputWidth = inputImage.width();
686 int inputHeight = inputImage.height();
687 if (inputWidth <= 0 || inputHeight <= 0 || inputImage.isNull()) {
690 }
691
692 int instances = inputFrame.instances();
693 if (instances < 1) {
696 }
697
698 err = av_frame_make_writable(
m_frame);
699 if (err != 0) {
702 }
703
705 inputHeight,
706 inputPixelFormat,
707 outputWidth,
708 outputHeight,
709 AVPixelFormat(
m_frame->format),
710 swsFlags);
711 if (!swsContext) {
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) {
723 }
725 }
726 }
727
729
732 }
733
736 }
737
739
742 }
743
745 if (err != 0) {
748 }
749
752 }
753
756
757
758 QFile *outputFile = qobject_cast<QFile *>(output.get());
759 if (outputFile && !outputFile->flush()) {
762 .arg(outputFile->errorString()));
764 }
765 output->close();
766
768 }
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)
AVFilterContext * m_buffersinkContext
AVFilterContext * m_buffersrcContext
static int64_t seekAvio(void *opaque, int64_t offset, int whence)
void setInternalErrorMessageAv(const QString &detail, int err)
AVFilterContext * m_palettesrcContext
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
static constexpr int BUFFER_SIZE
static constexpr int GIF_PALETTE_DIMENSION
static constexpr int GIF_PALETTE_BYTES