diff options
Diffstat (limited to 'libraries')
-rw-r--r-- | libraries/libopenshot/libopenshot.SlackBuild | 6 | ||||
-rw-r--r-- | libraries/libopenshot/libopenshot.info | 6 | ||||
-rw-r--r-- | libraries/libopenshot/limit-hwaccel.patch | 480 | ||||
-rw-r--r-- | libraries/libopenshot/resolve-abs.patch | 21 |
4 files changed, 508 insertions, 5 deletions
diff --git a/libraries/libopenshot/libopenshot.SlackBuild b/libraries/libopenshot/libopenshot.SlackBuild index cb04349871beb..3636b27ea6197 100644 --- a/libraries/libopenshot/libopenshot.SlackBuild +++ b/libraries/libopenshot/libopenshot.SlackBuild @@ -2,7 +2,7 @@ # Slackware build script for libopenshot -# Copyright 2016-2019 Christoph Willing, Brisbane Australia +# Copyright 2016-2020 Christoph Willing, Brisbane Australia # All rights reserved. # # Redistribution and use of this script, with or without modification, is @@ -23,7 +23,7 @@ # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PRGNAM=libopenshot -VERSION=${VERSION:-0.2.3} +VERSION=${VERSION:-0.2.4} BUILD=${BUILD:-1} TAG=${TAG:-_SBo} @@ -62,6 +62,8 @@ cd $TMP rm -rf $PRGNAM-$VERSION tar xvf $CWD/$PRGNAM-$VERSION.tar.gz cd $PRGNAM-$VERSION +patch -p1 < $CWD/limit-hwaccel.patch +patch -p1 < $CWD/resolve-abs.patch cp -p $CWD/zmq.hpp include/ chown -R root:root . find -L . \ diff --git a/libraries/libopenshot/libopenshot.info b/libraries/libopenshot/libopenshot.info index d1b65cdc5a474..32bf717caeb2d 100644 --- a/libraries/libopenshot/libopenshot.info +++ b/libraries/libopenshot/libopenshot.info @@ -1,9 +1,9 @@ PRGNAM="libopenshot" -VERSION="0.2.3" +VERSION="0.2.4" HOMEPAGE="https://github.com/OpenShot/libopenshot" -DOWNLOAD="https://github.com/OpenShot/libopenshot/archive/v0.2.3/libopenshot-0.2.3.tar.gz \ +DOWNLOAD="https://github.com/OpenShot/libopenshot/archive/v0.2.4/libopenshot-0.2.4.tar.gz \ https://raw.githubusercontent.com/zeromq/cppzmq/014628c/zmq.hpp" -MD5SUM="7ba70ff88ef52f658ce550aad1f5eaa1 \ +MD5SUM="91af3b0da2fbcffbef8fa78eeebec978 \ 56f264ec5604a5576e0d836d89c38c77" DOWNLOAD_x86_64="" MD5SUM_x86_64="" diff --git a/libraries/libopenshot/limit-hwaccel.patch b/libraries/libopenshot/limit-hwaccel.patch new file mode 100644 index 0000000000000..db6724228ba8e --- /dev/null +++ b/libraries/libopenshot/limit-hwaccel.patch @@ -0,0 +1,480 @@ +From e74d71f5451c6f2be9c546d61fac412f2d55f851 Mon Sep 17 00:00:00 2001 +From: "FeRD (Frank Dana)" <ferdnyc@gmail.com> +Date: Mon, 10 Feb 2020 01:50:31 -0500 +Subject: [PATCH] FFmpegReader/Writer: limit hwaccel to FFmpeg 3.4+ + +Add a new #define HAVE_HW_ACCEL, which is only set on FFmpeg 3.4+, +and use that to restrict the use of hw-accel features, leaving +IS_FFMPEG_3_2 to determine only whether code is compatible with +FFmpeg 3.2+. +--- + include/FFmpegReader.h | 7 ++-- + include/FFmpegUtilities.h | 4 +++ + src/FFmpegReader.cpp | 41 +++++++++++++---------- + src/FFmpegWriter.cpp | 70 +++++++++++++++++++++------------------ + 4 files changed, 68 insertions(+), 54 deletions(-) + +diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h +index 9faa86a3..cc782cd5 100644 +--- a/include/FFmpegReader.h ++++ b/include/FFmpegReader.h +@@ -98,7 +98,7 @@ namespace openshot { + AVFormatContext *pFormatCtx; + int i, videoStream, audioStream; + AVCodecContext *pCodecCtx, *aCodecCtx; +-#if (LIBAVFORMAT_VERSION_MAJOR >= 57) ++#if HAVE_HW_ACCEL + AVBufferRef *hw_device_ctx = NULL; //PM + #endif + AVStream *pStream, *aStream; +@@ -147,12 +147,11 @@ namespace openshot { + int64_t current_video_frame; // can't reliably use PTS of video to determine this + + int hw_de_supported = 0; // Is set by FFmpegReader +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + AVPixelFormat hw_de_av_pix_fmt = AV_PIX_FMT_NONE; + AVHWDeviceType hw_de_av_device_type = AV_HWDEVICE_TYPE_NONE; +-#endif +- + int IsHardwareDecodeSupported(int codecid); ++#endif + + /// Check for the correct frames per second value by scanning the 1st few seconds of video packets. + void CheckFPS(); +diff --git a/include/FFmpegUtilities.h b/include/FFmpegUtilities.h +index c673305e..62d64df1 100644 +--- a/include/FFmpegUtilities.h ++++ b/include/FFmpegUtilities.h +@@ -40,6 +40,10 @@ + #ifndef IS_FFMPEG_3_2 + #define IS_FFMPEG_3_2 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 64, 101)) + #endif ++ ++ #ifndef HAVE_HW_ACCEL ++ #define HAVE_HW_ACCEL (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 107, 100)) ++ #endif + + // Include the FFmpeg headers + extern "C" { +diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp +index 9234ecfe..b548fa8f 100644 +--- a/src/FFmpegReader.cpp ++++ b/src/FFmpegReader.cpp +@@ -35,13 +35,13 @@ + + #define ENABLE_VAAPI 0 + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + #pragma message "You are compiling with experimental hardware decode" + #else + #pragma message "You are compiling only with software decode" + #endif + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + #define MAX_SUPPORTED_WIDTH 1950 + #define MAX_SUPPORTED_HEIGHT 1100 + +@@ -71,14 +71,14 @@ typedef struct VAAPIDecodeContext { + enum AVPixelFormat surface_format; + int surface_count; + } VAAPIDecodeContext; +-#endif +-#endif ++#endif // ENABLE_VAAPI ++#endif // HAVE_HW_ACCEL + + + using namespace openshot; + + int hw_de_on = 0; +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + AVPixelFormat hw_de_av_pix_fmt_global = AV_PIX_FMT_NONE; + AVHWDeviceType hw_de_av_device_type_global = AV_HWDEVICE_TYPE_NONE; + #endif +@@ -153,7 +153,7 @@ bool AudioLocation::is_near(AudioLocation location, int samples_per_frame, int64 + return false; + } + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + + // Get hardware pix format + static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) +@@ -234,7 +234,7 @@ int FFmpegReader::IsHardwareDecodeSupported(int codecid) + } + return ret; + } +-#endif ++#endif // HAVE_HW_ACCEL + + void FFmpegReader::Open() { + // Open reader if not already open +@@ -287,7 +287,7 @@ void FFmpegReader::Open() { + // If hw accel is selected but hardware cannot handle repeat with software decoding + do { + pCodecCtx = AV_GET_CODEC_CONTEXT(pStream, pCodec); +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_de_on && (retry_decode_open==2)) { + // Up to here no decision is made if hardware or software decode + hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id); +@@ -304,7 +304,7 @@ void FFmpegReader::Open() { + + // Init options + av_dict_set(&opts, "strict", "experimental", 0); +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_de_on && hw_de_supported) { + // Open Hardware Acceleration + int i_decoder_hw = 0; +@@ -433,13 +433,13 @@ void FFmpegReader::Open() { + throw InvalidCodec("Hardware device create failed.", path); + } + } +-#endif ++#endif // HAVE_HW_ACCEL + + // Open video codec + if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0) + throw InvalidCodec("A video codec was found, but could not be opened.", path); + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_de_on && hw_de_supported) { + AVHWFramesConstraints *constraints = NULL; + void *hwconfig = NULL; +@@ -449,7 +449,7 @@ void FFmpegReader::Open() { + #if ENABLE_VAAPI + ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config; + constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig); +-#endif ++#endif // ENABLE_VAAPI + if (constraints) { + if (pCodecCtx->coded_width < constraints->min_width || + pCodecCtx->coded_height < constraints->min_height || +@@ -506,7 +506,7 @@ void FFmpegReader::Open() { + } + #else + retry_decode_open = 0; +-#endif ++#endif // HAVE_HW_ACCEL + } while (retry_decode_open); // retry_decode_open + // Free options + av_dict_free(&opts); +@@ -592,14 +592,14 @@ void FFmpegReader::Close() { + if (info.has_video) { + avcodec_flush_buffers(pCodecCtx); + AV_FREE_CONTEXT(pCodecCtx); +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_de_on) { + if (hw_device_ctx) { + av_buffer_unref(&hw_device_ctx); + hw_device_ctx = NULL; + } + } +-#endif ++#endif // HAVE_HW_ACCEL + } + if (info.has_audio) { + avcodec_flush_buffers(aCodecCtx); +@@ -1100,19 +1100,22 @@ bool FFmpegReader::GetAVFrame() { + + ret = avcodec_send_packet(pCodecCtx, packet); + ++ #if HAVE_HW_ACCEL + // Get the format from the variables set in get_hw_dec_format + hw_de_av_pix_fmt = hw_de_av_pix_fmt_global; + hw_de_av_device_type = hw_de_av_device_type_global; +- ++ #endif // HAVE_HW_ACCEL + if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Packet not sent)"); + } + else { + AVFrame *next_frame2; ++ #if HAVE_HW_ACCEL + if (hw_de_on && hw_de_supported) { + next_frame2 = AV_ALLOCATE_FRAME(); + } + else ++ #endif // HAVE_HW_ACCEL + { + next_frame2 = next_frame; + } +@@ -1125,6 +1128,7 @@ bool FFmpegReader::GetAVFrame() { + if (ret != 0) { + ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (invalid return frame received)"); + } ++ #if HAVE_HW_ACCEL + if (hw_de_on && hw_de_supported) { + int err; + if (next_frame2->format == hw_de_av_pix_fmt) { +@@ -1138,6 +1142,7 @@ bool FFmpegReader::GetAVFrame() { + } + } + else ++ #endif // HAVE_HW_ACCEL + { // No hardware acceleration used -> no copy from GPU memory needed + next_frame = next_frame2; + } +@@ -1151,9 +1156,11 @@ bool FFmpegReader::GetAVFrame() { + (AVPixelFormat)(pStream->codecpar->format), info.width, info.height); + } + } ++ #if HAVE_HW_ACCEL + if (hw_de_on && hw_de_supported) { + AV_FREE_FRAME(&next_frame2); + } ++ #endif // HAVE_HW_ACCEL + } + #else + avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet); +@@ -1169,7 +1176,7 @@ bool FFmpegReader::GetAVFrame() { + av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt, info.width, + info.height); + } +-#endif ++#endif // IS_FFMPEG_3_2 + } + + // deallocate the frame +diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp +index 245bd9bd..8d060d77 100644 +--- a/src/FFmpegWriter.cpp ++++ b/src/FFmpegWriter.cpp +@@ -35,7 +35,7 @@ + + using namespace openshot; + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + #pragma message "You are compiling with experimental hardware encode" + #else + #pragma message "You are compiling only with software encode" +@@ -44,7 +44,7 @@ using namespace openshot; + // Multiplexer parameters temporary storage + AVDictionary *mux_dict = NULL; + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + int hw_en_on = 1; // Is set in UI + int hw_en_supported = 0; // Is set by FFmpegWriter + AVPixelFormat hw_en_av_pix_fmt = AV_PIX_FMT_NONE; +@@ -81,7 +81,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 + av_buffer_unref(&hw_frames_ref); + return err; + } +-#endif ++#endif // HAVE_HW_ACCEL + + FFmpegWriter::FFmpegWriter(std::string path) : + path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), +@@ -171,7 +171,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f + if (codec.length() > 0) { + AVCodec *new_codec; + // Check if the codec selected is a hardware accelerated codec +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + #if defined(__linux__) + if (strstr(codec.c_str(), "_vaapi") != NULL) { + new_codec = avcodec_find_encoder_by_name(codec.c_str()); +@@ -225,7 +225,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f + #endif //__linux__ + #else // not ffmpeg 3 + new_codec = avcodec_find_encoder_by_name(codec.c_str()); +-#endif //IS_FFMPEG_3_2 ++#endif // HAVE_HW_ACCEL + if (new_codec == NULL) + throw InvalidCodec("A valid video codec could not be found for this file.", path); + else { +@@ -392,11 +392,11 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va + // This might be better in an extra methods as more options + // and way to set quality are possible + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) +- #if IS_FFMPEG_3_2 ++ #if HAVE_HW_ACCEL + if (hw_en_on) { + av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 + } else +- #endif ++ #endif // HAVE_HW_ACCEL + { + switch (c->codec_id) { + #if (LIBAVCODEC_VERSION_MAJOR >= 58) +@@ -442,7 +442,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va + // This might be better in an extra methods as more options + // and way to set quality are possible + #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on) { + double mbs = 15000000.0; + if (info.video_bit_rate > 0) { +@@ -455,7 +455,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va + } + c->bit_rate = (int)(mbs); + } else +-#endif ++#endif // HAVE_HW_ACCEL + { + switch (c->codec_id) { + #if (LIBAVCODEC_VERSION_MAJOR >= 58) +@@ -955,16 +955,14 @@ void FFmpegWriter::flush_encoders() { + // Close the video codec + void FFmpegWriter::close_video(AVFormatContext *oc, AVStream *st) + { +-#if IS_FFMPEG_3_2 +- // #if defined(__linux__) +- if (hw_en_on && hw_en_supported) { +- if (hw_device_ctx) { +- av_buffer_unref(&hw_device_ctx); +- hw_device_ctx = NULL; +- } ++#if HAVE_HW_ACCEL ++ if (hw_en_on && hw_en_supported) { ++ if (hw_device_ctx) { ++ av_buffer_unref(&hw_device_ctx); ++ hw_device_ctx = NULL; + } +- // #endif +-#endif ++ } ++#endif // HAVE_HW_ACCEL + } + + // Close the audio codec +@@ -1342,7 +1340,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { + // Set number of threads equal to number of processors (not to exceed 16) + video_codec->thread_count = std::min(FF_NUM_PROCESSORS, 16); + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + //char *dev_hw = NULL; + char adapter[256]; +@@ -1385,7 +1383,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { + throw InvalidCodec("Could not create hwdevice", path); + } + } +-#endif ++#endif // HAVE_HW_ACCEL + + /* find the video encoder */ + codec = avcodec_find_encoder_by_name(info.vcodec.c_str()); +@@ -1402,7 +1400,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { + AVDictionary *opts = NULL; + av_dict_set(&opts, "strict", "experimental", 0); + +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + video_codec->pix_fmt = hw_en_av_pix_fmt; + +@@ -1451,7 +1449,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { + "width", info.width, "height", info.height, av_err2str(err), -1); + } + } +-#endif ++#endif // HAVE_HW_ACCEL + + /* open the codec */ + if (avcodec_open2(video_codec, codec, &opts) < 0) +@@ -1900,14 +1898,17 @@ void FFmpegWriter::process_video_packet(std::shared_ptr<Frame> frame) { + frame_source = allocate_avframe(PIX_FMT_RGBA, source_image_width, source_image_height, &bytes_source, (uint8_t *) pixels); + #if IS_FFMPEG_3_2 + AVFrame *frame_final; ++ #if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + frame_final = allocate_avframe(AV_PIX_FMT_NV12, info.width, info.height, &bytes_final, NULL); +- } else { ++ } else ++ #endif // HAVE_HW_ACCEL ++ { + frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL); + } + #else + AVFrame *frame_final = allocate_avframe(video_codec->pix_fmt, info.width, info.height, &bytes_final, NULL); +-#endif ++#endif // IS_FFMPEG_3_2 + + // Fill with data + AV_COPY_PICTURE_DATA(frame_source, (uint8_t *) pixels, PIX_FMT_RGBA, source_image_width, source_image_height); +@@ -1977,7 +1978,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr<Frame> frame, AVFrame *fra + + // Assign the initial AVFrame PTS from the frame counter + frame_final->pts = write_video_count; +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + if (!(hw_frame = av_frame_alloc())) { + fprintf(stderr, "Error code: av_hwframe_alloc\n"); +@@ -1994,7 +1995,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr<Frame> frame, AVFrame *fra + } + av_frame_copy_props(hw_frame, frame_final); + } +-#endif ++#endif // HAVE_HW_ACCEL + /* encode the image */ + int got_packet_ptr = 0; + int error_code = 0; +@@ -2003,9 +2004,12 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr<Frame> frame, AVFrame *fra + int frameFinished = 0; + int ret; + ++ #if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + ret = avcodec_send_frame(video_codec, hw_frame); //hw_frame!!! +- } else { ++ } else ++ #endif // HAVE_HW_ACCEL ++ { + ret = avcodec_send_frame(video_codec, frame_final); + } + error_code = ret; +@@ -2062,8 +2066,8 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr<Frame> frame, AVFrame *fra + // got data back (so encode this frame) + got_packet_ptr = 1; + } +-#endif +-#endif ++#endif // LIBAVFORMAT_VERSION_MAJOR >= 54 ++#endif // IS_FFMPEG_3_2 + + /* if zero size, it means the image was buffered */ + if (error_code == 0 && got_packet_ptr) { +@@ -2095,14 +2099,14 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr<Frame> frame, AVFrame *fra + + // Deallocate packet + AV_FREE_PACKET(&pkt); +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + if (hw_frame) { + av_frame_free(&hw_frame); + hw_frame = NULL; + } + } +-#endif ++#endif // HAVE_HW_ACCEL + } + + // Success +@@ -2125,11 +2129,11 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) { + // Init software rescalers vector (many of them, one for each thread) + for (int x = 0; x < num_of_rescalers; x++) { + // Init the software scaler from FFMpeg +-#if IS_FFMPEG_3_2 ++#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_PIX_FMT_NV12, scale_mode, NULL, NULL, NULL); + } else +-#endif ++#endif // HAVE_HW_ACCEL + { + img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(video_st, video_st->codec), scale_mode, + NULL, NULL, NULL); diff --git a/libraries/libopenshot/resolve-abs.patch b/libraries/libopenshot/resolve-abs.patch new file mode 100644 index 0000000000000..917a9f085cce7 --- /dev/null +++ b/libraries/libopenshot/resolve-abs.patch @@ -0,0 +1,21 @@ +commit e39a1fc809d21d64ef8b96fcba6b56f554ccb550 +Author: Christoph Willing <chris.willing@linux.com> +Date: Mon Feb 10 18:28:56 2020 +1000 + + Resolve ambiguous abs() call + + Signed-off-by: Christoph Willing <chris.willing@linux.com> + +diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp +index c54bc7d..457ed9b 100644 +--- a/src/KeyFrame.cpp ++++ b/src/KeyFrame.cpp +@@ -71,7 +71,7 @@ namespace { + } + double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3]; + double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3]; +- if (abs(target - x) < allowed_error) { ++ if (fabs(target - x) < allowed_error) { + return y; + } + if (x > target) { |