From 41551f750b60a611ae08776438d3574ab3989feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Hyypp=C3=A4?= Date: Mon, 12 Feb 2024 22:57:52 +0200 Subject: [PATCH] qPCL: add support for (u)int64 pcd field types - update dependencies in conda.yml - PCL 1.13.1 supports int64 and uint64 field types - PCL dependency from vtk forced also to update ffmpeg - qAimation: update QVideoEncoder to use newer ffmpeg API --- .ci/conda.yml | 10 +-- .../extern/QTFFmpegWrapper/QVideoEncoder.cpp | 80 ++++++++++--------- .../qPCL/PclUtils/utils/my_point_types.h | 22 +++++ .../Standard/qPCL/PclUtils/utils/sm2cc.cpp | 30 ++++++- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/.ci/conda.yml b/.ci/conda.yml index 8ff199e92f..a9c1ac29e3 100644 --- a/.ci/conda.yml +++ b/.ci/conda.yml @@ -2,12 +2,12 @@ name: CloudCompareDev channels: - conda-forge dependencies: - - eigen=3.3.* - - ffmpeg=4.2.* + - eigen=3.4.* + - ffmpeg=6.1.* - ninja - - pcl=1.9.* - - pdal=2.1.* - - qt=5.12.* + - pcl=1.13.1 + - pdal=2.6.* + - qt=5.15.* - xerces-c=3.2.* - zlib=1.2.* - laszip diff --git a/plugins/core/Standard/qAnimation/extern/QTFFmpegWrapper/QVideoEncoder.cpp b/plugins/core/Standard/qAnimation/extern/QTFFmpegWrapper/QVideoEncoder.cpp index 606f38d0de..1d05bfe690 100644 --- a/plugins/core/Standard/qAnimation/extern/QTFFmpegWrapper/QVideoEncoder.cpp +++ b/plugins/core/Standard/qAnimation/extern/QTFFmpegWrapper/QVideoEncoder.cpp @@ -101,7 +101,7 @@ void QVideoEncoder::freeFrame() { if (m_ff->frame) { - av_free(m_ff->frame); + av_frame_free(&m_ff->frame); m_ff->frame = 0; } } @@ -111,10 +111,10 @@ bool QVideoEncoder::GetSupportedOutputFormats(std::vector& formats try { // list of all output formats - AVOutputFormat* prev = nullptr; + void *ofmt_opaque = nullptr; while (true) { - AVOutputFormat* format = av_oformat_next(prev); + const AVOutputFormat *format = av_muxer_iterate(&ofmt_opaque); if (format) { //potentially skip the output formats without any extension (= test formats mostly) @@ -130,7 +130,6 @@ bool QVideoEncoder::GetSupportedOutputFormats(std::vector& formats } formats.push_back(f); } - prev = format; } else { @@ -161,7 +160,7 @@ bool QVideoEncoder::open(QString formatShortName, QStringList& errors) return false; } - AVOutputFormat* outputFormat = NULL; + const AVOutputFormat *outputFormat = NULL; if (!formatShortName.isEmpty()) { outputFormat = av_guess_format(qPrintable(formatShortName), NULL, NULL); @@ -197,7 +196,7 @@ bool QVideoEncoder::open(QString formatShortName, QStringList& errors) AVCodecID codec_id = m_ff->formatContext->oformat->video_codec; //codec_id = AV_CODEC_ID_MPEG1VIDEO; //codec_id = AV_CODEC_ID_H264; - AVCodec *pCodec = avcodec_find_encoder(codec_id); + const AVCodec *pCodec = avcodec_find_encoder(codec_id); if (!pCodec) { errors << "Could not load the codec"; @@ -247,7 +246,7 @@ bool QVideoEncoder::open(QString formatShortName, QStringList& errors) return false; } m_ff->videoStream->id = m_ff->formatContext->nb_streams-1; - m_ff->videoStream->codec = m_ff->codecContext; + avcodec_parameters_from_context(m_ff->videoStream->codecpar, m_ff->codecContext); m_ff->videoStream->time_base.num = 1; m_ff->videoStream->time_base.den = m_fps; @@ -274,7 +273,7 @@ bool QVideoEncoder::open(QString formatShortName, QStringList& errors) } int err = avformat_write_header(m_ff->formatContext, NULL); - + if ( err != 0 ) { errors << QString("Could not write header for '%1'").arg(m_filename); @@ -293,7 +292,7 @@ static int write_frame(FFmpegStuffEnc* ff, AVPacket *pkt) assert(ff); return 0; } - + //if (ff->codecContext->coded_frame->key_frame) //{ // pkt->flags |= AV_PKT_FLAG_KEY; @@ -314,35 +313,33 @@ bool QVideoEncoder::close() return false; } + int ret = avcodec_send_frame(m_ff->codecContext, 0); + // delayed frames? - while (true) + while (ret >= 0) { AVPacket pkt; - memset( &pkt, 0, sizeof( AVPacket ) ); + memset(&pkt, 0, sizeof(AVPacket)); av_init_packet(&pkt); - int got_packet = 0; - int ret = avcodec_encode_video2(m_ff->codecContext, &pkt, 0, &got_packet); - if (ret < 0 || !got_packet) + ret = avcodec_receive_packet(m_ff->codecContext, &pkt); + if (ret < 0) { break; } write_frame(m_ff, &pkt); - - av_packet_unref(&pkt); } av_write_trailer(m_ff->formatContext); // close the codec - avcodec_close(m_ff->videoStream->codec); + avcodec_close(m_ff->codecContext); // free the streams and other data freeFrame(); for(unsigned i = 0; i < m_ff->formatContext->nb_streams; i++) { - av_freep(&m_ff->formatContext->streams[i]->codec); av_freep(&m_ff->formatContext->streams[i]); } @@ -372,18 +369,13 @@ bool QVideoEncoder::encodeImage(const QImage &image, int frameIndex, QString* er return false; } - AVPacket pkt; - memset( &pkt, 0, sizeof( AVPacket ) ); - av_init_packet(&pkt); - // encode the image - int got_packet = 0; { //compute correct timestamp based on the input frame index //int timestamp = ((m_ff->codecContext->time_base.num * 90000) / m_ff->codecContext->time_base.den) * frameIndex; m_ff->frame->pts = frameIndex/*timestamp*/; - int ret = avcodec_encode_video2(m_ff->codecContext, &pkt, m_ff->frame, &got_packet); + int ret = avcodec_send_frame(m_ff->codecContext, m_ff->frame); if (ret < 0) { char errorStr[AV_ERROR_MAX_STRING_SIZE] = {0}; @@ -392,23 +384,37 @@ bool QVideoEncoder::encodeImage(const QImage &image, int frameIndex, QString* er *errorString = QString("Error encoding video frame: %1").arg(errorStr); return false; } - } - if (got_packet) - { - int ret = write_frame(m_ff, &pkt); - if (ret < 0) + while (ret >= 0) { - char errorStr[AV_ERROR_MAX_STRING_SIZE] = {0}; - av_make_error_string(errorStr, AV_ERROR_MAX_STRING_SIZE, ret); - if (errorString) - *errorString = QString("Error while writing video frame: %1").arg(errorStr); - return false; + AVPacket pkt; + memset(&pkt, 0, sizeof(AVPacket)); + av_init_packet(&pkt); + + ret = avcodec_receive_packet(m_ff->codecContext, &pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + break; + else if (ret < 0) + { + char errorStr[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_make_error_string(errorStr, AV_ERROR_MAX_STRING_SIZE, ret); + if (errorString) + *errorString = QString("Error receiving video frame: %1").arg(errorStr); + return false; + } + + int wRet = write_frame(m_ff, &pkt); + if (wRet < 0) + { + char errorStr[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_make_error_string(errorStr, AV_ERROR_MAX_STRING_SIZE, wRet); + if (errorString) + *errorString = QString("Error while writing video frame: %1").arg(errorStr); + return false; + } } } - av_packet_unref(&pkt); - return true; } @@ -421,7 +427,7 @@ bool QVideoEncoder::convertImage_sws(const QImage &image, QString* errorString/* *errorString = "Wrong image size"; return false; } - + QImage::Format format = image.format(); if ( format != QImage::Format_RGB32 && format != QImage::Format_ARGB32 diff --git a/plugins/core/Standard/qPCL/PclUtils/utils/my_point_types.h b/plugins/core/Standard/qPCL/PclUtils/utils/my_point_types.h index 3db301f295..927fdc1247 100644 --- a/plugins/core/Standard/qPCL/PclUtils/utils/my_point_types.h +++ b/plugins/core/Standard/qPCL/PclUtils/utils/my_point_types.h @@ -110,6 +110,20 @@ struct UInt8Scalar }; +struct Int64Scalar +{ + std::int64_t S5c4laR; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + +}; + +struct UInt64Scalar +{ + std::uint64_t S5c4laR; + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + +}; + //! PCL custom point type used for reading intensity data struct OnlyNormals { @@ -232,6 +246,14 @@ POINT_CLOUD_REGISTER_POINT_STRUCT (UInt8Scalar, (std::uint8_t, S5c4laR, S5c4laR) ) +POINT_CLOUD_REGISTER_POINT_STRUCT (Int64Scalar, + (std::int64_t, S5c4laR, S5c4laR) + ) + +POINT_CLOUD_REGISTER_POINT_STRUCT (UInt64Scalar, + (std::uint64_t, S5c4laR, S5c4laR) + ) + POINT_CLOUD_REGISTER_POINT_STRUCT (OnlyNormals, (float, normal_x, normal_x) (float, normal_y, normal_y) diff --git a/plugins/core/Standard/qPCL/PclUtils/utils/sm2cc.cpp b/plugins/core/Standard/qPCL/PclUtils/utils/sm2cc.cpp index 8b86f1a4dc..5a2edabc10 100644 --- a/plugins/core/Standard/qPCL/PclUtils/utils/sm2cc.cpp +++ b/plugins/core/Standard/qPCL/PclUtils/utils/sm2cc.cpp @@ -76,7 +76,7 @@ POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZTpl, (double, x, x) (double, y, y) (double, z, z) ) - + static size_t GetNumberOfPoints(const PCLCloud& pclCloud) { return static_cast(pclCloud.width) * pclCloud.height; @@ -211,7 +211,7 @@ bool pcl2cc::CopyNormals(const PCLCloud& pclCloud, ccPointCloud& ccCloud) } ccCloud.showNormals(true); - + return true; } @@ -379,6 +379,32 @@ bool pcl2cc::CopyScalarField( const PCLCloud& pclCloud, } break; + case PCLScalarField::UINT64: + { + pcl::PointCloud pclScalar; + FROM_PCL_CLOUD(pclCloud, pclScalar); + + for (unsigned i = 0; i < pointCount; ++i) + { + ScalarType scalar = static_cast(pclScalar.points[i].S5c4laR); + newSF->addElement(scalar); + } + } + break; + + case PCLScalarField::INT64: + { + pcl::PointCloud pclScalar; + FROM_PCL_CLOUD(pclCloud, pclScalar); + + for (unsigned i = 0; i < pointCount; ++i) + { + ScalarType scalar = static_cast(pclScalar.points[i].S5c4laR); + newSF->addElement(scalar); + } + } + break; + default: ccLog::Warning(QString("[PCL] Field with an unmanaged type (= %1)").arg(pclField.datatype)); newSF->release();