Skip to content
Snippets Groups Projects
nvpipe_decoder.cpp 4.23 KiB
Newer Older
#include <ftl/codecs/nvpipe_decoder.hpp>

#include <loguru.hpp>

#include <ftl/cuda_util.hpp>
Nicolas Pope's avatar
Nicolas Pope committed
#include <ftl/codecs/hevc.hpp>
#include <ftl/codecs/h264.hpp>
//#include <cuda_runtime.h>
#include <opencv2/core/cuda/common.hpp>

using ftl::codecs::NvPipeDecoder;

NvPipeDecoder::NvPipeDecoder() {
	nv_decoder_ = nullptr;
Nicolas Pope's avatar
Nicolas Pope committed
	seen_iframe_ = false;
}

NvPipeDecoder::~NvPipeDecoder() {
	if (nv_decoder_ != nullptr) {
		NvPipe_Destroy(nv_decoder_);
	}
}

bool NvPipeDecoder::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) return false;
	bool is_float_frame = out.type() == CV_32F;

	// Is the previous decoder still valid for current resolution and type?
	if (nv_decoder_ != nullptr && (last_definition_ != pkt.definition || is_float_channel_ != is_float_frame)) {
		NvPipe_Destroy(nv_decoder_);
		nv_decoder_ = nullptr;
	}

	is_float_channel_ = is_float_frame;
	last_definition_ = pkt.definition;

	//LOG(INFO) << "DECODE OUT: " << out.rows << ", " << out.type();
Sebastian Hahta's avatar
Sebastian Hahta committed
	//LOG(INFO) << "DECODE RESOLUTION: (" << (int)pkt.definition << ") " << ftl::codecs::getWidth(pkt.definition) << "x" << ftl::codecs::getHeight(pkt.definition);

	// Build a decoder instance of the correct kind
	if (nv_decoder_ == nullptr) {
		nv_decoder_ = NvPipe_CreateDecoder(
				(is_float_frame) ? NVPIPE_UINT16 : NVPIPE_RGBA32,
				(pkt.codec == codec_t::HEVC) ? NVPIPE_HEVC : NVPIPE_H264,
				ftl::codecs::getWidth(pkt.definition),
				ftl::codecs::getHeight(pkt.definition));
		if (!nv_decoder_) {
			//LOG(INFO) << "Bitrate=" << (int)bitrate << " width=" << ABRController::getColourWidth(bitrate);
			LOG(FATAL) << "Could not create decoder: " << NvPipe_GetError(NULL);
		}
Nicolas Pope's avatar
Nicolas Pope committed

		seen_iframe_ = false;
Sebastian Hahta's avatar
Sebastian Hahta committed
	
	// TODO: (Nick) Move to member variable to prevent re-creation
	tmp_.create(cv::Size(ftl::codecs::getWidth(pkt.definition),ftl::codecs::getHeight(pkt.definition)), (is_float_frame) ? CV_16U : CV_8UC4);
	if (!seen_iframe_) {
		if (pkt.codec == ftl::codecs::codec_t::HEVC) {
			if (ftl::codecs::hevc::isIFrame(pkt.data)) seen_iframe_ = true;
		} else if (pkt.codec == ftl::codecs::codec_t::H264) {
			if (ftl::codecs::h264::isIFrame(pkt.data)) seen_iframe_ = true;
		}
Nicolas Pope's avatar
Nicolas Pope committed
	}

	// No I-Frame yet so don't attempt to decode P-Frames.
Nicolas Pope's avatar
Nicolas Pope committed
	if (!seen_iframe_) return false;

	// Final checks for validity
	if (pkt.data.size() == 0 || tmp_.size() != out.size()) { // || !ftl::codecs::hevc::validNAL(pkt.data)) {
		LOG(ERROR) << "Failed to decode packet";
		return false;
	}

	int rc = NvPipe_Decode(nv_decoder_, pkt.data.data(), pkt.data.size(), tmp_.data, tmp_.cols, tmp_.rows, tmp_.step);
	if (rc == 0) LOG(ERROR) << "NvPipe decode error: " << NvPipe_GetError(nv_decoder_);

	if (is_float_frame) {
		// Is the received frame the same size as requested output?
		//if (out.rows == ftl::codecs::getHeight(pkt.definition)) {
			tmp_.convertTo(out, CV_32FC1, 1.0f/1000.0f, stream_);
		/*} else {
			LOG(WARNING) << "Resizing decoded frame from " << tmp_.size() << " to " << out.size();
			// FIXME: This won't work on GPU
			tmp_.convertTo(tmp_, CV_32FC1, 1.0f/1000.0f, stream_);
			cv::cuda::resize(tmp_, out, out.size(), 0, 0, cv::INTER_NEAREST, stream_);
	} else {
		// Is the received frame the same size as requested output?
		//if (out.rows == ftl::codecs::getHeight(pkt.definition)) {
			// Flag 0x1 means frame is in RGB so needs conversion to BGR
			if (pkt.flags & 0x1) {
				cv::cuda::cvtColor(tmp_, out, cv::COLOR_RGBA2BGRA, 0, stream_);
			} else {
				//cv::cuda::cvtColor(tmp_, out, cv::COLOR_BGRA2BGR, 0, stream_);
		/*} else {
			LOG(WARNING) << "Resizing decoded frame from " << tmp_.size() << " to " << out.size();
			// FIXME: This won't work on GPU, plus it allocates extra memory...
			// Flag 0x1 means frame is in RGB so needs conversion to BGR
			if (pkt.flags & 0x1) {
				cv::cuda::cvtColor(tmp_, tmp_, cv::COLOR_RGBA2BGR, 0, stream_);
			} else {
				cv::cuda::cvtColor(tmp_, tmp_, cv::COLOR_BGRA2BGR, 0, stream_);
			cv::cuda::resize(tmp_, out, out.size(), 0.0, 0.0, cv::INTER_LINEAR, stream_);
	stream_.waitForCompletion();

	return rc > 0;
}

bool NvPipeDecoder::accepts(const ftl::codecs::Packet &pkt) {
	return pkt.codec == codec_t::HEVC || pkt.codec == codec_t::H264;