diff --git a/components/codecs/include/ftl/codecs/bitrates.hpp b/components/codecs/include/ftl/codecs/bitrates.hpp index fd50b9c86fb8b4828ee70e1c0f0f766d9112b121..d34ede8adb88c647949051853dde33f81221a8d2 100644 --- a/components/codecs/include/ftl/codecs/bitrates.hpp +++ b/components/codecs/include/ftl/codecs/bitrates.hpp @@ -24,7 +24,9 @@ enum struct codec_t : uint8_t { POSE, // 4x4 eigen matrix MSGPACK, STRING, // Null terminated string - RAW // Some unknown binary format + RAW, // Some unknown binary format + + Any = 255 }; /** diff --git a/components/codecs/include/ftl/codecs/encoder.hpp b/components/codecs/include/ftl/codecs/encoder.hpp index 560810b84060b3b062b426614da40836634fdee5..fed8d95755859d2c9c71ee1475f89d024bf38811 100644 --- a/components/codecs/include/ftl/codecs/encoder.hpp +++ b/components/codecs/include/ftl/codecs/encoder.hpp @@ -33,7 +33,8 @@ enum class device_t { */ Encoder *allocateEncoder( ftl::codecs::definition_t maxdef=ftl::codecs::definition_t::HD1080, - ftl::codecs::device_t dev=ftl::codecs::device_t::Any); + ftl::codecs::device_t dev=ftl::codecs::device_t::Any, + ftl::codecs::codec_t codec=ftl::codecs::codec_t::Any); /** * Release an encoder to be reused by some other stream. @@ -47,7 +48,7 @@ void free(Encoder *&e); class Encoder { public: friend Encoder *allocateEncoder(ftl::codecs::definition_t, - ftl::codecs::device_t); + ftl::codecs::device_t, ftl::codecs::codec_t); friend void free(Encoder *&); public: @@ -86,6 +87,8 @@ class Encoder { virtual void reset() {} + virtual bool supports(ftl::codecs::codec_t codec)=0; + protected: bool available; const ftl::codecs::definition_t max_definition; diff --git a/components/codecs/include/ftl/codecs/nvpipe_encoder.hpp b/components/codecs/include/ftl/codecs/nvpipe_encoder.hpp index 3f8de66cf7b77265e245a30da4522936058426c0..c2fe379dc985488f014e911dd1e096431bdee96e 100644 --- a/components/codecs/include/ftl/codecs/nvpipe_encoder.hpp +++ b/components/codecs/include/ftl/codecs/nvpipe_encoder.hpp @@ -25,11 +25,16 @@ class NvPipeEncoder : public ftl::codecs::Encoder { void reset(); + bool supports(ftl::codecs::codec_t codec) override; + + static constexpr int kFlagRGB = 0x00000001; + private: NvPipe *nvenc_; definition_t current_definition_; bool is_float_channel_; bool was_reset_; + ftl::codecs::codec_t preference_; bool _encoderMatch(const cv::Mat &in, definition_t def); bool _createEncoder(const cv::Mat &in, definition_t def, bitrate_t rate); diff --git a/components/codecs/include/ftl/codecs/opencv_encoder.hpp b/components/codecs/include/ftl/codecs/opencv_encoder.hpp index a34f8811de62277b5be53dd329517deb3ed06af3..aabe9247c81d2dbcb365ad259d3f44bfce6b7f95 100644 --- a/components/codecs/include/ftl/codecs/opencv_encoder.hpp +++ b/components/codecs/include/ftl/codecs/opencv_encoder.hpp @@ -28,6 +28,8 @@ class OpenCVEncoder : public ftl::codecs::Encoder { bool encode(const cv::Mat &in, ftl::codecs::definition_t definition, ftl::codecs::bitrate_t bitrate, const std::function<void(const ftl::codecs::Packet&)>&) override; + bool supports(ftl::codecs::codec_t codec) override; + //bool encode(const cv::cuda::GpuMat &in, std::vector<uint8_t> &out, bitrate_t bix, bool); private: diff --git a/components/codecs/include/ftl/codecs/packet.hpp b/components/codecs/include/ftl/codecs/packet.hpp index 89df44f9e0128cea76b15adf331796644cb91c6b..f2fa53c8fa860527f1939b61c4962b306da89043 100644 --- a/components/codecs/include/ftl/codecs/packet.hpp +++ b/components/codecs/include/ftl/codecs/packet.hpp @@ -16,7 +16,7 @@ namespace codecs { */ struct Header { const char magic[4] = {'F','T','L','F'}; - uint8_t version = 2; + uint8_t version = 3; }; /** diff --git a/components/codecs/include/ftl/codecs/reader.hpp b/components/codecs/include/ftl/codecs/reader.hpp index cdc50cad30bc07fcc7793b24fb4daf1b308b03a8..69e19f0a5eac16fdb52c4b823f042e38fb71e0e7 100644 --- a/components/codecs/include/ftl/codecs/reader.hpp +++ b/components/codecs/include/ftl/codecs/reader.hpp @@ -49,6 +49,7 @@ class Reader { bool has_data_; int64_t timestart_; bool playing_; + int version_; MUTEX mtx_; diff --git a/components/codecs/src/encoder.cpp b/components/codecs/src/encoder.cpp index 834c003b706be7208b627e3ea03d7b5efbb7245e..1863222d6944e5bc5dfbea9fd8aa90d4fdc821f7 100644 --- a/components/codecs/src/encoder.cpp +++ b/components/codecs/src/encoder.cpp @@ -11,6 +11,7 @@ using ftl::codecs::bitrate_t; using ftl::codecs::definition_t; using ftl::codecs::preset_t; using ftl::codecs::device_t; +using ftl::codecs::codec_t; using ftl::codecs::kPresetBest; using ftl::codecs::kPresetWorst; using ftl::codecs::kPresetLQThreshold; @@ -34,7 +35,7 @@ using namespace ftl::codecs::internal; static MUTEX mutex; Encoder *ftl::codecs::allocateEncoder(ftl::codecs::definition_t maxdef, - ftl::codecs::device_t dev) { + ftl::codecs::device_t dev, ftl::codecs::codec_t codec) { UNIQUE_LOCK(mutex, lk); if (!has_been_init) init_encoders(); @@ -43,6 +44,7 @@ Encoder *ftl::codecs::allocateEncoder(ftl::codecs::definition_t maxdef, if (!e->available) continue; if (dev != device_t::Any && dev != e->device) continue; if (maxdef != definition_t::Any && (maxdef < e->max_definition || maxdef > e->min_definition)) continue; + if (codec != codec_t::Any && !e->supports(codec)) continue; e->available = false; return e; diff --git a/components/codecs/src/nvpipe_decoder.cpp b/components/codecs/src/nvpipe_decoder.cpp index 2ecd69e7cd857ac2e2ca3ffd30f6b6a7dca972c2..97985cd20c4bd2dea7aa86c829b3b151563c3cfb 100644 --- a/components/codecs/src/nvpipe_decoder.cpp +++ b/components/codecs/src/nvpipe_decoder.cpp @@ -50,7 +50,7 @@ void cropAndScaleUp(cv::Mat &in, cv::Mat &out) { bool NvPipeDecoder::decode(const ftl::codecs::Packet &pkt, cv::Mat &out) { cudaSetDevice(0); UNIQUE_LOCK(mutex_,lk); - if (pkt.codec != codec_t::HEVC) return false; + 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? @@ -68,7 +68,7 @@ bool NvPipeDecoder::decode(const ftl::codecs::Packet &pkt, cv::Mat &out) { if (nv_decoder_ == nullptr) { nv_decoder_ = NvPipe_CreateDecoder( (is_float_frame) ? NVPIPE_UINT16 : NVPIPE_RGBA32, - NVPIPE_HEVC, + (pkt.codec == codec_t::HEVC) ? NVPIPE_HEVC : NVPIPE_H264, ftl::codecs::getWidth(pkt.definition), ftl::codecs::getHeight(pkt.definition)); if (!nv_decoder_) { @@ -88,6 +88,7 @@ bool NvPipeDecoder::decode(const ftl::codecs::Packet &pkt, cv::Mat &out) { // Obtain NAL unit type if (ftl::codecs::hevc::isIFrame(pkt.data)) seen_iframe_ = true; } + // TODO: Parse H264 for i-frame check if (!seen_iframe_) return false; @@ -106,10 +107,18 @@ bool NvPipeDecoder::decode(const ftl::codecs::Packet &pkt, cv::Mat &out) { } else { // Is the received frame the same size as requested output? if (out.rows == ftl::codecs::getHeight(pkt.definition)) { - cv::cvtColor(tmp, out, cv::COLOR_BGRA2BGR); + if (pkt.flags & 0x1) { + cv::cvtColor(tmp, out, cv::COLOR_RGBA2BGR); + } else { + cv::cvtColor(tmp, out, cv::COLOR_BGRA2BGR); + } } else { LOG(WARNING) << "Resizing decoded frame from " << tmp.size() << " to " << out.size(); - cv::cvtColor(tmp, tmp, cv::COLOR_BGRA2BGR); + if (pkt.flags & 0x1) { + cv::cvtColor(tmp, tmp, cv::COLOR_RGBA2BGR); + } else { + cv::cvtColor(tmp, tmp, cv::COLOR_BGRA2BGR); + } cv::resize(tmp, out, out.size()); } } diff --git a/components/codecs/src/nvpipe_encoder.cpp b/components/codecs/src/nvpipe_encoder.cpp index 80bde1f9fac0dc63fc68db845b7e53084f6c59ec..28407a6583d39f3f9091dd6e5fd52c991d562e3e 100644 --- a/components/codecs/src/nvpipe_encoder.cpp +++ b/components/codecs/src/nvpipe_encoder.cpp @@ -20,6 +20,7 @@ NvPipeEncoder::NvPipeEncoder(definition_t maxdef, current_definition_ = definition_t::HD1080; is_float_channel_ = false; was_reset_ = false; + preference_ = codec_t::Any; } NvPipeEncoder::~NvPipeEncoder() { @@ -30,6 +31,14 @@ void NvPipeEncoder::reset() { was_reset_ = true; } +bool NvPipeEncoder::supports(ftl::codecs::codec_t codec) { + switch (codec) { + case codec_t::H264: + case codec_t::HEVC: preference_ = codec; return true; + default: return false; + } +} + /* Check preset resolution is not better than actual resolution. */ definition_t NvPipeEncoder::_verifiedDefinition(definition_t def, const cv::Mat &in) { int height = ftl::codecs::getHeight(def); @@ -103,9 +112,12 @@ bool NvPipeEncoder::encode(const cv::Mat &in, definition_t odefinition, bitrate_ if (tmp.type() == CV_32F) { tmp.convertTo(tmp, CV_16UC1, 1000); } else if (tmp.type() == CV_8UC3) { - cv::cvtColor(tmp, tmp, cv::COLOR_BGR2BGRA); + cv::cvtColor(tmp, tmp, cv::COLOR_BGR2RGBA); + } else if (tmp.type() == CV_8UC4) { + cv::cvtColor(tmp, tmp, cv::COLOR_BGRA2RGBA); } else { - //in.copyTo(tmp); + LOG(ERROR) << "Unsupported cv::Mat type in Nvidia encoder"; + return false; } // scale/pad to fit output format @@ -114,10 +126,11 @@ bool NvPipeEncoder::encode(const cv::Mat &in, definition_t odefinition, bitrate_ //std::swap(tmp, tmp2); Packet pkt; - pkt.codec = codec_t::HEVC; + pkt.codec = (preference_ == codec_t::Any) ? codec_t::HEVC : preference_; pkt.definition = definition; pkt.block_total = 1; pkt.block_number = 0; + pkt.flags = NvPipeEncoder::kFlagRGB; pkt.data.resize(ftl::codecs::kVideoBufferSize); uint64_t cs = NvPipe_Encode( @@ -147,9 +160,35 @@ bool NvPipeEncoder::_encoderMatch(const cv::Mat &in, definition_t def) { ((in.type() == CV_8UC3 || in.type() == CV_8UC4) && !is_float_channel_)) && current_definition_ == def; } +static uint64_t calculateBitrate(definition_t def, bitrate_t rate) { + float scale = 1.0f; + switch (rate) { + case bitrate_t::High : break; + case bitrate_t::Standard : scale = 0.5f; break; + case bitrate_t::Low : scale = 0.25f; break; + } + + float bitrate = 1.0f; // Megabits + switch (def) { + case definition_t::UHD4k : bitrate = 24.0f; break; + case definition_t::HTC_VIVE : bitrate = 16.0f; break; + case definition_t::HD1080 : bitrate = 16.0f; break; + case definition_t::HD720 : bitrate = 8.0f; break; + case definition_t::SD576 : + case definition_t::SD480 : bitrate = 4.0f; break; + case definition_t::LD360 : bitrate = 2.0f; break; + default : bitrate = 8.0f; + } + + return uint64_t(bitrate * 1000.0f * 1000.0f * scale); +} + bool NvPipeEncoder::_createEncoder(const cv::Mat &in, definition_t def, bitrate_t rate) { if (_encoderMatch(in, def) && nvenc_) return true; + uint64_t bitrate = calculateBitrate(def, rate); + LOG(INFO) << "Calculated bitrate: " << bitrate; + if (in.type() == CV_32F) is_float_channel_ = true; else is_float_channel_ = false; current_definition_ = def; @@ -158,9 +197,9 @@ bool NvPipeEncoder::_createEncoder(const cv::Mat &in, definition_t def, bitrate_ const int fps = 1000/ftl::timer::getInterval(); nvenc_ = NvPipe_CreateEncoder( (is_float_channel_) ? NVPIPE_UINT16 : NVPIPE_RGBA32, - NVPIPE_HEVC, + (preference_ == codec_t::Any || preference_ == codec_t::HEVC) ? NVPIPE_HEVC : NVPIPE_H264, (is_float_channel_) ? NVPIPE_LOSSLESS : NVPIPE_LOSSY, - 16*1000*1000, + bitrate, fps, // FPS ftl::codecs::getWidth(def), // Output Width ftl::codecs::getHeight(def) // Output Height diff --git a/components/codecs/src/opencv_encoder.cpp b/components/codecs/src/opencv_encoder.cpp index 028395b9e32865adb7a907d148308c9085588fa3..1ab7a7a3150d99cdec475deb2497f7ff7b013b1e 100644 --- a/components/codecs/src/opencv_encoder.cpp +++ b/components/codecs/src/opencv_encoder.cpp @@ -20,6 +20,14 @@ OpenCVEncoder::~OpenCVEncoder() { } +bool OpenCVEncoder::supports(ftl::codecs::codec_t codec) { + switch (codec) { + case codec_t::JPG: + case codec_t::PNG: return true; + default: return false; + } +} + bool OpenCVEncoder::encode(const cv::Mat &in, definition_t definition, bitrate_t bitrate, const std::function<void(const ftl::codecs::Packet&)> &cb) { cv::Mat tmp; bool is_colour = in.type() != CV_32F; diff --git a/components/codecs/src/reader.cpp b/components/codecs/src/reader.cpp index 53225519a572858a8ceb96e42e9b58ee0c839d43..3727bdd3882d4930c8f8c59e7f67035b818334c4 100644 --- a/components/codecs/src/reader.cpp +++ b/components/codecs/src/reader.cpp @@ -27,6 +27,8 @@ bool Reader::begin() { (*stream_).read((char*)&ih, sizeof(ih)); } + version_ = h.version; + // Capture current time to adjust timestamps timestart_ = (ftl::timer::get_time() / ftl::timer::getInterval()) * ftl::timer::getInterval(); playing_ = true; @@ -86,6 +88,11 @@ bool Reader::read(int64_t ts, const std::function<void(const ftl::codecs::Stream // Adjust timestamp get<0>(data).timestamp += timestart_; + // Fix to clear flags for version 2. + if (version_ == 2) { + get<1>(data).flags = 0; + } + // TODO: Need to read ahead a few frames because there may be a // smaller timestamp after this one... requires a buffer. Ideally this // should be resolved during the write process. diff --git a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp index cfa552a862e1d46b9600ce523815eb8fa5efa001..1fa1cb48663b1ecaa47966df6d68cfe4663f8b9b 100644 --- a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp @@ -158,6 +158,7 @@ class Streamer : public ftl::Configurable { ftl::timer::TimerHandle timer_job_; ftl::codecs::device_t hq_devices_; + ftl::codecs::codec_t hq_codec_; enum class Quality { High, diff --git a/components/rgbd-sources/src/streamer.cpp b/components/rgbd-sources/src/streamer.cpp index 6e5a3273da6c4ef7f07197c1e3cbf3f4624642a2..3eff9e24f55af859ab2e24c7ccafac32c8872656 100644 --- a/components/rgbd-sources/src/streamer.cpp +++ b/components/rgbd-sources/src/streamer.cpp @@ -45,6 +45,7 @@ Streamer::Streamer(nlohmann::json &config, Universe *net) encode_mode_ = ftl::rgbd::kEncodeVideo; hq_devices_ = (value("disable_hardware_encode", false)) ? device_t::Software : device_t::Any; + hq_codec_ = value("video_codec", ftl::codecs::codec_t::Any); //group_.setFPS(value("fps", 20)); group_.setLatency(4); @@ -446,9 +447,9 @@ void Streamer::_process(ftl::rgbd::FrameSet &fs) { // Do we need to do high quality encoding? if (src->hq_count > 0) { if (!src->hq_encoder_c1) src->hq_encoder_c1 = ftl::codecs::allocateEncoder( - definition_t::HD1080, hq_devices_); + definition_t::HD1080, hq_devices_, hq_codec_); if (!src->hq_encoder_c2 && hasChan2) src->hq_encoder_c2 = ftl::codecs::allocateEncoder( - definition_t::HD1080, hq_devices_); + definition_t::HD1080, hq_devices_, hq_codec_); // Do we have the resources to do a HQ encoding? if (src->hq_encoder_c1 && (!hasChan2 || src->hq_encoder_c2)) {