diff --git a/components/codecs/include/ftl/codecs/nvidia_decoder.hpp b/components/codecs/include/ftl/codecs/nvidia_decoder.hpp index 2c3ad8efaa7be45eedd831eb6d1341c3963576a6..d904ba1b64218653d4751e4da453fcd3242159a7 100644 --- a/components/codecs/include/ftl/codecs/nvidia_decoder.hpp +++ b/components/codecs/include/ftl/codecs/nvidia_decoder.hpp @@ -25,7 +25,7 @@ class NvidiaDecoder : public ftl::codecs::Decoder { ftl::codecs::codec_t last_codec_; MUTEX mutex_; bool seen_iframe_; - cv::cuda::GpuMat tmp_; + cv::cuda::GpuMat buffer_; int width_; int height_; int last_width_; diff --git a/components/codecs/src/nvidia_decoder.cpp b/components/codecs/src/nvidia_decoder.cpp index b4638ad048c3e7aa5cf65f021bbcffd73e62540a..063bc384998e1fb10a28db3dc589cc6b859de073 100644 --- a/components/codecs/src/nvidia_decoder.cpp +++ b/components/codecs/src/nvidia_decoder.cpp @@ -115,13 +115,16 @@ uint8_t* NvidiaDecoder::_decode(const uint8_t* src, uint64_t srcSize) { bool NvidiaDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out) { //cudaSetDevice(0); UNIQUE_LOCK(mutex_,lk); - if (pkt.codec != codec_t::HEVC && pkt.codec != codec_t::H264 && pkt.codec != codec_t::HEVC_LOSSLESS && pkt.codec != codec_t::H264_LOSSLESS) return false; + if (pkt.codec != codec_t::HEVC && pkt.codec != codec_t::H264 && pkt.codec != codec_t::HEVC_LOSSLESS && pkt.codec != codec_t::H264_LOSSLESS) { + LOG(ERROR) << "Bad codec: " << int(pkt.codec); + return false; + } bool is_float_frame = pkt.flags & ftl::codecs::kFlagFloat; bool islossless = ((pkt.codec == ftl::codecs::codec_t::HEVC || pkt.codec == ftl::codecs::codec_t::H264) && is_float_frame && !(pkt.flags & 0x2)) || pkt.codec == ftl::codecs::codec_t::HEVC_LOSSLESS || pkt.codec == ftl::codecs::codec_t::H264_LOSSLESS; - if (is_float_frame && out.type() != CV_32F) { + /*if (is_float_frame && out.type() != CV_32F) { LOG(ERROR) << "Invalid buffer for float frame"; return false; } @@ -129,7 +132,7 @@ bool NvidiaDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out if (!is_float_frame && out.type() != CV_8UC4) { LOG(ERROR) << "Invalid buffer for lossy colour frame: " << out.type(); return false; - } + }*/ _create(pkt); @@ -175,10 +178,10 @@ bool NvidiaDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out width_ = nv_decoder_->GetWidth(); height_ = nv_decoder_->GetHeight(); - if (out.cols != ((is_float_frame && islossless) ? width_/2 : width_) || out.rows != height_) { + /*if (out.cols != ((is_float_frame && islossless) ? width_/2 : width_) || out.rows != height_) { LOG(ERROR) << "Decoded frame not same size as buffer: " << width_ << "x" << height_ << " -> " << out.cols << "x" << out.rows; return false; - } + }*/ // OpenCV GpuMat for YCbCr 4:2:0 cv::cuda::GpuMat surface; @@ -190,14 +193,23 @@ bool NvidiaDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out if (is_float_frame) { if (!islossless) { + buffer_.create(height_, width_, CV_32F); + out = buffer_; + cv::cuda::GpuMat sroi = surface(cv::Rect(0,0,width_, height_)); cv::cuda::GpuMat csroi = surface(cv::Rect(0,height_,width_, height_/2)); ftl::cuda::vuya_to_depth(out, sroi, csroi, 16.0f, cvstream); } else { + buffer_.create(height_, width_/2, CV_32F); + out = buffer_; + ftl::cuda::nv12_to_float(decodedPtr, width_, (float*)out.data, static_cast<uint32_t>(out.step1()), width_/2, height_, stream_); } } else { + buffer_.create(height_, width_, CV_8UC4); + out = buffer_; + // Flag 0x1 means frame is in RGB so needs conversion to BGR if (pkt.flags & 0x1) { Nv12ToColor32<BGRA32>(decodedPtr, width_, out.data, static_cast<int>(out.step1()), width_, height_, 0, stream_); diff --git a/components/codecs/test/nvidia_codec_unit.cpp b/components/codecs/test/nvidia_codec_unit.cpp index f72ef049b9774f865963f8b4d2b16179c09dc046..937106bb3891736e0c7c37eba7c1c8da7e3489ae 100644 --- a/components/codecs/test/nvidia_codec_unit.cpp +++ b/components/codecs/test/nvidia_codec_unit.cpp @@ -316,21 +316,6 @@ TEST_CASE( "NvidiaDecoder::decode() - corrupted packet" ) { cv::cuda::GpuMat in; cv::cuda::GpuMat out; - SECTION("Bad output size") { - in = cv::cuda::GpuMat(cv::Size(2560,720), CV_8UC4, cv::Scalar(255,0,0,0)); - out = cv::cuda::GpuMat(cv::Size(2500,720), CV_8UC4, cv::Scalar(0,0,0,0)); - - ftl::codecs::Packet pkt; - pkt.codec = codec_t::Any; - pkt.bitrate = 255; - pkt.frame_count = 2; - pkt.flags = 0; - bool r = encoder.encode(in, pkt); - - REQUIRE( r ); - REQUIRE( !decoder.decode(pkt, out) ); - } - SECTION("Corrupted but supported codec") { in = cv::cuda::GpuMat(cv::Size(2560,720), CV_8UC4, cv::Scalar(255,0,0,0)); out = cv::cuda::GpuMat(cv::Size(2560,720), CV_8UC4, cv::Scalar(0,0,0,0)); @@ -379,7 +364,8 @@ TEST_CASE( "NvidiaDecoder::decode() - corrupted packet" ) { pkt.flags = ftl::codecs::kFlagFloat; REQUIRE( r ); - REQUIRE( !decoder.decode(pkt, out) ); + REQUIRE( decoder.decode(pkt, out) ); + REQUIRE( out.type() == CV_32F ); } SECTION("Corrupted float mapped flags") { @@ -414,7 +400,9 @@ TEST_CASE( "NvidiaDecoder::decode() - corrupted packet" ) { pkt.flags = 0; REQUIRE( r ); - REQUIRE( !decoder.decode(pkt, out) ); + REQUIRE( decoder.decode(pkt, out) ); + REQUIRE( out.type() == CV_8UC4 ); + REQUIRE( out.cols == 2*in.cols ); } SECTION("Missing data") { diff --git a/components/streams/src/receiver.cpp b/components/streams/src/receiver.cpp index dc13bc52987c9e41e8fe701594221e7faee3912a..c0cdbf04f890d5beea81b1873059a1257cb7e610 100644 --- a/components/streams/src/receiver.cpp +++ b/components/streams/src/receiver.cpp @@ -255,38 +255,13 @@ void Receiver::_processVideo(const StreamPacket &spkt, const Packet &pkt) { auto [tx,ty] = ftl::codecs::chooseTileConfig(pkt.frame_count); - int width = ividstate.width; //calibration.width; - int height = ividstate.height; //calibration.height; - - if (width <= 0 || height <= 0 || width > 9000 || height > 9000) { - // Attempt to retry the decode later - // Make a copy of the packets into a thread job - // FIXME: Check that thread pool does not explode - if (spkt.retry_count < 10) { - LOG(WARNING) << "No calibration, retrying: " << spkt.timestamp; - ftl::pool.push([this, spkt, pkt](int id) mutable { - ++const_cast<StreamPacket&>(spkt).retry_count; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - _processVideo(spkt, pkt); - }); - } else { - LOG(WARNING) << "No calibration, failed frame: " << spkt.timestamp; - } - return; - } - if (tx == 0 || ty == 0) { LOG(ERROR) << "No Packets"; return; } - auto &surface = ividstate.surface[static_cast<int>(spkt.channel)]; - - // Allocate a decode surface, this is a tiled image to be split later - int cvtype = ftl::codecs::type(spkt.channel); - surface.create(height*ty, width*tx, cvtype); - - bool is_static = ividstate.decoders[channum] && (spkt.hint_capability & ftl::codecs::kStreamCap_Static); + cv::cuda::GpuMat surface; + //bool is_static = ividstate.decoders[channum] && (spkt.hint_capability & ftl::codecs::kStreamCap_Static); // Find or create the decoder _createDecoder(ividstate, channum, pkt); @@ -297,17 +272,30 @@ void Receiver::_processVideo(const StreamPacket &spkt, const Packet &pkt) { } // Do the actual decode into the surface buffer - if (!is_static) { - try { - FTL_Profile("Decode", 0.015); - if (!decoder->decode(pkt, surface)) { - LOG(ERROR) << "Decode failed on channel " << (int)spkt.channel; - return; - } - } catch (std::exception &e) { - LOG(ERROR) << "Decode failed for " << spkt.timestamp << ": " << e.what(); + try { + FTL_Profile("Decode", 0.015); + if (!decoder->decode(pkt, surface)) { + LOG(ERROR) << "Decode failed on channel " << (int)spkt.channel; return; } + } catch (std::exception &e) { + LOG(ERROR) << "Decode failed for " << spkt.timestamp << ": " << e.what(); + return; + } + + int width = surface.cols / tx; + int height = surface.rows / ty; + + if (width == 0 || height == 0) { + LOG(ERROR) << "Invalid decoded size: " << surface.cols << "x" << surface.rows << " (" << tx << "," << ty << ")"; + return; + } + + int cvtype = ftl::codecs::type(spkt.channel); + + if (surface.type() != cvtype) { + LOG(ERROR) << "Invalid video format received"; + return; } // Get the frameset