647{
649
650 QTemporaryFile tempFile;
651 QString tempFilePath;
652 if (tempFile.open()) {
653 tempFilePath = tempFile.fileName();
654 tempFile.close();
655 } else {
656 warnFile <<
"Failed to open temporary file:" << tempFile.errorString();
657
658 }
659
660 Context ctx(&outErrorMessage);
663
664
665 std::unique_ptr<Audio> audio;
666 if (!
settings().audioFile.isEmpty()) {
667
668
669
670
671
672
673 audio = std::make_unique<Audio>(mlt_audio_s16, format->audioSampleRate(), 2);
674 if (!audio->open(ctx,
settings())) {
676 }
677
678 switch (audio->pullFrame(ctx)) {
680 break;
682
683
684 audio.reset();
685 break;
686 default:
688 }
689 }
690
691
692 {
693 QJniObject outputPath = QJniObject::fromString(
settings().outputFile);
694 if (ctx.checkObject(QStringLiteral("outputPath"), outputPath)) {
696 }
697
698 QJniObject tempPath = QJniObject::fromString(tempFilePath);
699 if (ctx.checkObject(QStringLiteral("tempPath"), tempPath)) {
701 }
702
703 QJniObject videoEncoderName =
704 QJniObject::fromString(format->getVideoEncoderPreference(
settings().formatPreferences));
705 ctx.checkException(QStringLiteral("videoEncoderName"));
706
707 jint audioSampleRate;
708 jint audioChannelCount;
709 QJniObject audioEncoderName;
710 QJniObject audioFormatName;
711 if (audio) {
712 audioSampleRate = jint(audio->sampleRate());
713 audioChannelCount = jint(audio->channelCount());
714 audioEncoderName = QJniObject::fromString(format->getAudioEncoderPreference(
settings().formatPreferences));
715 ctx.checkException(QStringLiteral("audioEncoderName"));
716 audioFormatName = QJniObject::fromString(audio->formatName());
717 ctx.checkException(QStringLiteral("audioFormatName"));
718 } else {
719 audioSampleRate = jint(0);
720 audioChannelCount = jint(0);
721 }
722
723 ctx.setEncoder(QJniObject(
724 "org/krita/android/VideoEncoder",
725 "(IIIFLjava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;III)V",
726 jint(format->formatId()),
727 jint(outputWidth),
728 jint(outputHeight),
730 outputPath.object<jstring>(),
731 tempPath.object<jstring>(),
732 videoEncoderName.object<jstring>(),
733 jint(format->getVideoBitratePreference(
settings().formatPreferences)),
734 audio ? audioEncoderName.object<jstring>() : nullptr,
735 audio ? audioFormatName.object<jstring>() : nullptr,
736 jint(audioSampleRate),
737 jint(audioChannelCount),
738 jint(format->getAudioBitratePreference(
settings().formatPreferences))));
739 if (ctx.checkObject(QStringLiteral("encoder"), ctx.encoder())) {
741 }
742 }
743
746 }
747
748
749 {
750 QJniObject activity = QJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
751 "activity",
752 "()Landroid/app/Activity;");
753 if (ctx.checkObject(QStringLiteral("activity"), activity)) {
755 }
756
757 int startResult =
758 int(ctx.encoder().callMethod<jint>("start", "(Landroid/content/Context;)I", activity.object<jobject>()));
759 if (ctx.checkResult(QStringLiteral("start"), startResult)) {
761 }
762 }
763
764
765 Frame frame;
766 int swsFlags = ctx.getSwsFlags(
settings().scaleFilter);
770 }
771
772
773 QImage inputImage;
774 if (!frame.readImage(inputImage)) {
775
776
777
778
779
780 if (audio) {
781 ctx.setInternalErrorMessage(QStringLiteral("failed to read frame %1").arg(frame.path()));
783 } else {
784 continue;
785 }
786 }
787
788 AVPixelFormat inputPixelFormat;
789 if (!ctx.convertFrame(inputImage, inputPixelFormat)) {
790
791
792 if (audio) {
793 ctx.setInternalErrorMessage(QStringLiteral("failed to convert frame %1").arg(frame.path()));
795 } else {
796 continue;
797 }
798 }
799
800 int instances = frame.instances();
801 for (int i = 0; i < instances; ++i) {
802
805 return prepareVideoResult;
806 }
807
808
809 EncoderImage encoderImage;
812 }
813
814
815
816
817
818 uint8_t *dstBuffers[4] = {nullptr, nullptr, nullptr, nullptr};
819 int dstLinesizes[4] = {0, 0, 0, 0};
820 AVPixelFormat outputPixelFormat;
821 if (encoderImage.pixelStrideU == 1 && encoderImage.pixelStrideV == 1) {
822
823 outputPixelFormat = AV_PIX_FMT_YUV420P;
824 dstBuffers[0] = encoderImage.bufferY;
825 dstBuffers[1] = encoderImage.bufferU;
826 dstBuffers[2] = encoderImage.bufferV;
827 dstLinesizes[0] = encoderImage.rowStrideY;
828 dstLinesizes[1] = encoderImage.rowStrideU;
829 dstLinesizes[2] = encoderImage.rowStrideV;
830
831 } else if (encoderImage.pixelStrideU == 2 && encoderImage.pixelStrideV == 2
832 && encoderImage.bufferU + 1 == encoderImage.bufferV) {
833
834 outputPixelFormat = AV_PIX_FMT_NV12;
835 dstBuffers[0] = encoderImage.bufferY;
836 dstBuffers[1] = encoderImage.bufferU;
837 dstLinesizes[0] = encoderImage.rowStrideY;
838 dstLinesizes[1] = encoderImage.rowStrideU;
839
840 } else if (encoderImage.pixelStrideU == 2 && encoderImage.pixelStrideV == 2
841 && encoderImage.bufferV + 1 == encoderImage.bufferU) {
842
843 outputPixelFormat = AV_PIX_FMT_NV21;
844 dstBuffers[0] = encoderImage.bufferY;
845 dstBuffers[1] = encoderImage.bufferV;
846 dstLinesizes[0] = encoderImage.rowStrideY;
847 dstLinesizes[1] = encoderImage.rowStrideV;
848
849 } else {
850 ctx.setInternalErrorMessage(QStringLiteral("unknown buffer format u%1/%2 v%3/%4")
851 .arg(quintptr(encoderImage.bufferU), 0, 16)
852 .arg(encoderImage.pixelStrideU)
853 .arg(quintptr(encoderImage.bufferV), 0, 16)
854 .arg(encoderImage.pixelStrideV));
856 }
857
858 SwsContext *swsContext = ctx.getSwsContextFor(inputImage.width(),
859 inputImage.height(),
860 inputPixelFormat,
861 outputWidth,
862 outputHeight,
863 outputPixelFormat,
864 swsFlags);
865 if (!swsContext) {
866 ctx.setInternalErrorMessage(QStringLiteral("sws_getCachedContext"));
868 }
869
870 if (instances == 1) {
871
872 const uint8_t *srcBuffers[] = {inputImage.bits(), nullptr, nullptr, nullptr};
873 const int srcLinesizes[] = {inputImage.bytesPerLine(), 0, 0, 0};
874 sws_scale(swsContext, srcBuffers, srcLinesizes, 0, inputImage.height(), dstBuffers, dstLinesizes);
875
876 } else {
877
878
879 if (i == 0 || ctx.imageFormat() != outputPixelFormat) {
880 if (ctx.imageFormat() != outputPixelFormat) {
881 if (!ctx.allocateImage(outputWidth, outputHeight, outputPixelFormat)) {
882 warnFile <<
"Encoder changed pixel format from" << int(ctx.imageFormat()) <<
"to"
883 << int(outputPixelFormat);
885 }
886 }
887
888 const uint8_t *srcBuffers[] = {inputImage.bits(), nullptr, nullptr, nullptr};
889 const int srcLinesizes[] = {inputImage.bytesPerLine(), 0, 0, 0};
890 sws_scale(swsContext,
891 srcBuffers,
892 srcLinesizes,
893 0,
894 inputImage.height(),
895 ctx.imageBuffers(),
896 ctx.imageLinesizes());
897 }
898
899 av_image_copy2(dstBuffers,
900 dstLinesizes,
901 ctx.imageBuffers(),
902 ctx.imageLinesizes(),
903 outputPixelFormat,
904 outputWidth,
905 outputHeight);
906 }
907
908 int commitResult = int(ctx.encoder().callMethod<jint>("commitVideo", "()I"));
909 if (ctx.checkResult(QStringLiteral("commitVideo"), commitResult)) {
911 }
912
913 if (audio && !audio->isFinished()) {
914 int pullFrameResult = audio->pullFrame(ctx);
916 do {
919 return prepareAudioResult;
920 }
921
922 if (!audio->pushSamples(ctx)) {
924 }
925 } while (audio->isSampleDataRemaining());
926 audio->finishFrame();
927
931 return prepareAudioResult;
932 }
933
934 if (!audio->finish(ctx)) {
936 }
937
938 } else {
940 }
941 }
942 }
943 }
944
947 }
948
949
950 {
951
954 return prepareVideoResult;
955 }
956
957
958 int finishVideoResult = int(ctx.encoder().callMethod<jint>("finishVideo", "()I"));
959 if (ctx.checkResult(QStringLiteral("finishVideo"), finishVideoResult)) {
961 }
962
963
964 if (audio && !audio->isFinished()) {
967 return prepareAudioResult;
968 }
969
970 if (!audio->finish(ctx)) {
972 }
973 }
974 }
975
976
977 bool videoStreamEnded = false;
978 bool audioStreamEnded = !audio;
979 do {
980 if (!videoStreamEnded) {
981 int drainVideoResult =
drainVideo(ctx, 1000000LL);
983 videoStreamEnded = true;
988 } else {
990 }
991 }
992 if (!audioStreamEnded) {
993 int drainAudioResult =
drainAudio(ctx, 1000000LL);
995 audioStreamEnded = true;
1000 } else {
1002 }
1003 }
1004 } while (!videoStreamEnded || !audioStreamEnded);
1005
1006
1007 {
1008 int closeResult = int(ctx.encoder().callMethod<jint>("close", "()I"));
1009 if (ctx.checkResult(QStringLiteral("close"), closeResult)) {
1011 }
1012
1015 }
1016
1018 QString copyErrorMessage;
1020 ctx.setInternalErrorMessage(copyErrorMessage);
1022 }
1023 }
1024 }
1025
1027}
bool copyFile(const QString &inputPath, const QString &outputPath, QString *outErrorMessage)