diff --git a/src/encoder/encoder.hpp b/src/encoder/encoder.hpp index a97e2b616e3b44681f8645b2e6773a3fa2d1460e..7afa7c00bac0cde6b5c9c91f3b2431e7b1b03b6a 100644 --- a/src/encoder/encoder.hpp +++ b/src/encoder/encoder.hpp @@ -1,5 +1,5 @@ /* - The encoder can be used to create a h264 stream out of a sequence of images. + The encoder can be used to create an encoded stream out of a sequence of images. In order to be able to use the encoder, it is neccessary to call the function setup_instance_for_encoder(...) during the setup of the vulkan instance. Besides that, it is neccessary to call the function setup_device_for_encoder(...) during the setup of the vulkan device. A frame can be submitted for encoding using the function encode(...). @@ -35,6 +35,12 @@ #include <memory> #include <cstdint> +enum EncoderCodec +{ + ENCODER_CODEC_H264, + ENCODER_CODEC_H265 +}; + enum EncoderMode { ENCODER_MODE_CONSTANT_BITRATE, @@ -87,7 +93,7 @@ public: Encoder() = default; virtual ~Encoder() = default; - virtual bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) = 0; + virtual bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format, EncoderCodec codec) = 0; virtual void destroy() = 0; // The following functions should be thread safe. @@ -108,6 +114,7 @@ public: virtual void set_frame_rate(uint32_t frame_rate); // The frame rate is given in frames per second. Default: 90 // The following functions should be thread safe. + virtual EncoderCodec get_codec() const = 0; virtual EncoderMode get_mode() const; virtual double get_quality() const; virtual double get_bitrate() const; diff --git a/src/encoder/nvidia_encoder.cpp b/src/encoder/nvidia_encoder.cpp index 0823eaf8c958f3ea4ed00da107162e7d1b9217d8..f75b02024c6b4ec528c8cd65522e89933fd66e86 100644 --- a/src/encoder/nvidia_encoder.cpp +++ b/src/encoder/nvidia_encoder.cpp @@ -31,16 +31,17 @@ NvidiaEncoder::NvidiaEncoder() : worker_pool(1) } -bool NvidiaEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) +bool NvidiaEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format, EncoderCodec codec) { this->device = device; + this->codec = codec; if (!this->create_context(device)) { return false; } - if (!this->create_session(size)) + if (!this->create_session(size, codec)) { return false; } @@ -232,6 +233,11 @@ void NvidiaEncoder::set_frame_rate(uint32_t frame_rate) this->config_change = true; } +EncoderCodec NvidiaEncoder::get_codec() const +{ + return this->codec; +} + EncoderMode NvidiaEncoder::get_mode() const { return this->mode; @@ -500,44 +506,6 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) return; } - NV_ENC_PIC_PARAMS_MVC motion_vector_parameters; - motion_vector_parameters.version = NV_ENC_PIC_PARAMS_MVC_VER; - motion_vector_parameters.viewID = 0; - motion_vector_parameters.temporalID = 0; - motion_vector_parameters.priorityID = 0; - memset(motion_vector_parameters.reserved1, 0, sizeof(motion_vector_parameters.reserved1)); - memset(motion_vector_parameters.reserved2, 0, sizeof(motion_vector_parameters.reserved2)); - - NV_ENC_PIC_PARAMS_H264_EXT codec_extention_parameters; - codec_extention_parameters.mvcPicParams = motion_vector_parameters; - memset(codec_extention_parameters.reserved1, 0, sizeof(codec_extention_parameters.reserved1)); - - NV_ENC_PIC_PARAMS_H264 codec_parameters; - codec_parameters.displayPOCSyntax = 0; - codec_parameters.reserved3 = 0; - codec_parameters.refPicFlag = 0; - codec_parameters.colourPlaneId = 0; - codec_parameters.forceIntraRefreshWithFrameCnt = 0; - codec_parameters.constrainedFrame = 1; - codec_parameters.sliceModeDataUpdate = 1; - codec_parameters.ltrMarkFrame = 0; - codec_parameters.ltrUseFrames = 0; - codec_parameters.reservedBitFields = 0; - codec_parameters.sliceTypeData = 0; - codec_parameters.sliceTypeArrayCnt = 0; - codec_parameters.seiPayloadArrayCnt = 0; - codec_parameters.seiPayloadArray = nullptr; - codec_parameters.sliceMode = 3; - codec_parameters.sliceModeData = 10; - codec_parameters.ltrMarkFrameIdx = 0; - codec_parameters.ltrUseFrameBitmap = 0; - codec_parameters.ltrUsageMode = 0; - codec_parameters.forceIntraSliceCount = 0; - codec_parameters.forceIntraSliceIdx = 0; - codec_parameters.h264ExtPicParams = codec_extention_parameters; - memset(codec_parameters.reserved, 0, sizeof(codec_parameters.reserved)); - memset(codec_parameters.reserved2, 0, sizeof(codec_parameters.reserved2)); - NV_ENC_PIC_PARAMS encode_parameters; encode_parameters.version = NV_ENC_PIC_PARAMS_VER; encode_parameters.inputWidth = frame->image_size.x; @@ -553,7 +521,6 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) encode_parameters.bufferFmt = map_info.mappedBufferFmt; encode_parameters.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; encode_parameters.pictureType = (NV_ENC_PIC_TYPE)0; - encode_parameters.codecPicParams.h264PicParams = codec_parameters; memset(encode_parameters.meHintCountsPerBlock, 0, sizeof(encode_parameters.meHintCountsPerBlock)); encode_parameters.meExternalHints = nullptr; memset(encode_parameters.reserved1, 0, sizeof(encode_parameters.reserved1)); @@ -566,6 +533,83 @@ void NvidiaEncoder::submit_encode_task(NvidiaEncoderFrame::Ptr frame) memset(encode_parameters.reserved3, 0, sizeof(encode_parameters.reserved3)); memset(encode_parameters.reserved4, 0, sizeof(encode_parameters.reserved4)); + if(this->codec == ENCODER_CODEC_H264) + { + NV_ENC_PIC_PARAMS_MVC motion_vector_parameters; + motion_vector_parameters.version = NV_ENC_PIC_PARAMS_MVC_VER; + motion_vector_parameters.viewID = 0; + motion_vector_parameters.temporalID = 0; + motion_vector_parameters.priorityID = 0; + memset(motion_vector_parameters.reserved1, 0, sizeof(motion_vector_parameters.reserved1)); + memset(motion_vector_parameters.reserved2, 0, sizeof(motion_vector_parameters.reserved2)); + + NV_ENC_PIC_PARAMS_H264_EXT codec_extention_parameters; + codec_extention_parameters.mvcPicParams = motion_vector_parameters; + memset(codec_extention_parameters.reserved1, 0, sizeof(codec_extention_parameters.reserved1)); + + NV_ENC_PIC_PARAMS_H264 codec_parameters; + codec_parameters.displayPOCSyntax = 0; + codec_parameters.reserved3 = 0; + codec_parameters.refPicFlag = 0; + codec_parameters.colourPlaneId = 0; + codec_parameters.forceIntraRefreshWithFrameCnt = 0; + codec_parameters.constrainedFrame = 1; + codec_parameters.sliceModeDataUpdate = 1; + codec_parameters.ltrMarkFrame = 0; + codec_parameters.ltrUseFrames = 0; + codec_parameters.reservedBitFields = 0; + codec_parameters.sliceTypeData = 0; + codec_parameters.sliceTypeArrayCnt = 0; + codec_parameters.seiPayloadArrayCnt = 0; + codec_parameters.seiPayloadArray = nullptr; + codec_parameters.sliceMode = 3; + codec_parameters.sliceModeData = 10; + codec_parameters.ltrMarkFrameIdx = 0; + codec_parameters.ltrUseFrameBitmap = 0; + codec_parameters.ltrUsageMode = 0; + codec_parameters.forceIntraSliceCount = 0; + codec_parameters.forceIntraSliceIdx = 0; + codec_parameters.h264ExtPicParams = codec_extention_parameters; + memset(codec_parameters.reserved, 0, sizeof(codec_parameters.reserved)); + memset(codec_parameters.reserved2, 0, sizeof(codec_parameters.reserved2)); + + encode_parameters.codecPicParams.h264PicParams = codec_parameters; + } + + else if(this->codec == ENCODER_CODEC_H265) + { + NV_ENC_PIC_PARAMS_HEVC codec_parameters; + codec_parameters.displayPOCSyntax = 0; + codec_parameters.refPicFlag = 0; + codec_parameters.temporalId = 0; + codec_parameters.forceIntraRefreshWithFrameCnt = 0; + codec_parameters.constrainedFrame = 1; + codec_parameters.sliceModeDataUpdate = 1; + codec_parameters.ltrMarkFrame = 0; + codec_parameters.ltrUseFrames = 0; + codec_parameters.reservedBitFields = 0; + codec_parameters.sliceTypeData = 0; + codec_parameters.sliceTypeArrayCnt = 0; + codec_parameters.sliceMode = 3; + codec_parameters.sliceModeData = 10; + codec_parameters.ltrMarkFrameIdx = 0; + codec_parameters.ltrUseFrameBitmap = 0; + codec_parameters.ltrUsageMode = 0; + codec_parameters.seiPayloadArrayCnt = 0; + codec_parameters.reserved = 0; + codec_parameters.seiPayloadArray = nullptr; + memset(codec_parameters.reserved2, 0, sizeof(codec_parameters.reserved2)); + memset(codec_parameters.reserved3, 0, sizeof(codec_parameters.reserved3)); + + encode_parameters.codecPicParams.hevcPicParams = codec_parameters; + } + + else + { + lava::log()->error("Nvidia Encoder: Invalid codec!"); + this->on_encode_error(); + } + if (nvenc_functions.nvEncEncodePicture(this->nvenc_session, &encode_parameters) != NV_ENC_SUCCESS) { lava::log()->error("Nvidia Encoder: Can't encoder image!"); @@ -647,7 +691,7 @@ void NvidiaEncoder::destroy_context() } } -bool NvidiaEncoder::create_session(const glm::uvec2& size) +bool NvidiaEncoder::create_session(const glm::uvec2& size, EncoderCodec codec) { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_parameters; session_parameters.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; @@ -665,28 +709,46 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) return false; } - if (!this->check_encode_support(NV_ENC_CODEC_H264_GUID)) + GUID codec_guid; + GUID profile_guid; + + switch(codec) + { + case ENCODER_CODEC_H264: + codec_guid = NV_ENC_CODEC_H264_GUID; + profile_guid = NV_ENC_H264_PROFILE_HIGH_GUID; + break; + case ENCODER_CODEC_H265: + codec_guid = NV_ENC_CODEC_HEVC_GUID; + profile_guid = NV_ENC_HEVC_PROFILE_MAIN_GUID; + break; + default: + lava::log()->error("Nvidia Encoder: Invalid codec!"); + return false; + } + + if (!this->check_encode_support(codec_guid)) { lava::log()->error("Nvidia Encoder: Codec not supported!"); return false; } - if (!this->check_profile_support(NV_ENC_CODEC_H264_GUID, NV_ENC_H264_PROFILE_HIGH_GUID)) + if (!this->check_profile_support(codec_guid, profile_guid)) { lava::log()->error("Nvidia Encoder: Profile not supported!"); return false; } - if (!this->check_preset_support(NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P1_GUID)) + if (!this->check_preset_support(codec_guid, NV_ENC_PRESET_P1_GUID)) { lava::log()->error("Nvidia Encoder: Preset not supported!"); return false; } - if (!this->check_format_support(NV_ENC_CODEC_H264_GUID, NV_ENC_BUFFER_FORMAT_ABGR)) + if (!this->check_format_support(codec_guid, NV_ENC_BUFFER_FORMAT_ABGR)) { lava::log()->error("Nvidia Encoder: Input format not supported!"); @@ -701,7 +763,7 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) memset(preset_config.reserved1, 0, sizeof(preset_config.reserved1)); memset(preset_config.reserved2, 0, sizeof(preset_config.reserved2)); - if (nvenc_functions.nvEncGetEncodePresetConfigEx(this->nvenc_session, NV_ENC_CODEC_H264_GUID, NV_ENC_PRESET_P1_GUID, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, &preset_config) != NV_ENC_SUCCESS) + if (nvenc_functions.nvEncGetEncodePresetConfigEx(this->nvenc_session, codec_guid, NV_ENC_PRESET_P1_GUID, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY, &preset_config) != NV_ENC_SUCCESS) { lava::log()->error("Nvidia Encoder: Can't get preset for config!"); @@ -709,19 +771,33 @@ bool NvidiaEncoder::create_session(const glm::uvec2& size) } this->nvenc_encode_config = preset_config.presetCfg; - this->nvenc_encode_config.profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; + this->nvenc_encode_config.profileGUID = profile_guid; this->nvenc_encode_config.rcParams.version = NV_ENC_RC_PARAMS_VER; this->nvenc_encode_config.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; - - this->nvenc_encode_config.encodeCodecConfig.h264Config.disableSPSPPS = 1; - this->nvenc_encode_config.encodeCodecConfig.h264Config.enableIntraRefresh = 1; //NOTE: Can create problems when replayed in VLC - this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshPeriod = this->frame_rate * 2; - this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshCnt = 10; this->nvenc_encode_config.rcParams.enableAQ = 1; this->nvenc_encode_config.rcParams.aqStrength = 0; + switch(codec) + { + case ENCODER_CODEC_H264: + this->nvenc_encode_config.encodeCodecConfig.h264Config.disableSPSPPS = 1; + this->nvenc_encode_config.encodeCodecConfig.h264Config.enableIntraRefresh = 1; //NOTE: Can create problems when replayed in VLC + this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshPeriod = this->frame_rate * 2; + this->nvenc_encode_config.encodeCodecConfig.h264Config.intraRefreshCnt = 10; + break; + case ENCODER_CODEC_H265: + this->nvenc_encode_config.encodeCodecConfig.hevcConfig.disableSPSPPS = 1; + this->nvenc_encode_config.encodeCodecConfig.hevcConfig.enableIntraRefresh = 1; + this->nvenc_encode_config.encodeCodecConfig.hevcConfig.intraRefreshPeriod = this->frame_rate * 2; + this->nvenc_encode_config.encodeCodecConfig.hevcConfig.intraRefreshCnt = 10; + break; + default: + lava::log()->error("Nvidia Encoder: Invalid codec!"); + return false; + } + this->nvenc_session_config.version = NV_ENC_INITIALIZE_PARAMS_VER; - this->nvenc_session_config.encodeGUID = NV_ENC_CODEC_H264_GUID; + this->nvenc_session_config.encodeGUID = codec_guid; this->nvenc_session_config.presetGUID = NV_ENC_PRESET_P1_GUID; this->nvenc_session_config.encodeWidth = size.x; this->nvenc_session_config.encodeHeight = size.y; diff --git a/src/encoder/nvidia_encoder.hpp b/src/encoder/nvidia_encoder.hpp index 4a364f1e940ec323903ab2d1ae0a5f53fb860048..ceb40737b0d68300fd794e7a923864acd3843149 100644 --- a/src/encoder/nvidia_encoder.hpp +++ b/src/encoder/nvidia_encoder.hpp @@ -50,7 +50,7 @@ public: public: NvidiaEncoder(); - bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) override; + bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format, EncoderCodec codec) override; void destroy() override; EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) override; @@ -62,6 +62,7 @@ public: void set_bitrate(double bitrate) override; void set_frame_rate(uint32_t frame_rate) override; + EncoderCodec get_codec() const override; EncoderMode get_mode() const override; double get_quality() const override; double get_bitrate() const override; @@ -81,7 +82,7 @@ private: bool create_context(lava::device_ptr device); void destroy_context(); - bool create_session(const glm::uvec2& size); + bool create_session(const glm::uvec2& size, EncoderCodec codec); void destroy_session(); bool create_input_buffer(NvidiaEncoderFrame::Ptr frame, lava::device_ptr device, const glm::uvec2& size, EncoderFormat format); @@ -97,6 +98,7 @@ private: private: OnEncodeError on_encode_error; + EncoderCodec codec = ENCODER_CODEC_H264; EncoderMode mode = ENCODER_MODE_CONSTANT_QUALITY; double quality = 0.0; double bitrate = 5.0; diff --git a/src/encoder/vulkan_encoder.cpp b/src/encoder/vulkan_encoder.cpp index 48a17447effad710a54f7f0e9856513ee2a5a0d3..36da40d2dc9ae8556940eca328f17c1565f8d4ba 100644 --- a/src/encoder/vulkan_encoder.cpp +++ b/src/encoder/vulkan_encoder.cpp @@ -7,8 +7,15 @@ VulkanEncoder::VulkanEncoder() : worker_pool(1) } -bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) +bool VulkanEncoder::create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format, EncoderCodec codec) { + if (codec != ENCODER_CODEC_H264) + { + lava::log()->error("Vulkan encoder only supportes h264 encoding!"); + + return false; + } + this->frame_count = VULKAN_ENCODER_FRAMES; this->frame_index = 0; @@ -334,6 +341,11 @@ void VulkanEncoder::set_frame_rate(uint32_t frame_rate) this->control_change = true; } +EncoderCodec VulkanEncoder::get_codec() const +{ + return ENCODER_CODEC_H264; +} + EncoderMode VulkanEncoder::get_mode() const { return this->setting_mode; diff --git a/src/encoder/vulkan_encoder.hpp b/src/encoder/vulkan_encoder.hpp index cea0f938b75bbcab1369a64da352ca22a50444ab..126af55968620fb6b191a60f25c2ec5aaec5a5a7 100644 --- a/src/encoder/vulkan_encoder.hpp +++ b/src/encoder/vulkan_encoder.hpp @@ -77,7 +77,7 @@ public: public: VulkanEncoder(); - bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format) override; + bool create(lava::device_ptr device, const lava::renderer& renderer, const glm::uvec2& size, EncoderFormat format, EncoderCodec codec) override; void destroy() override; EncoderResult encode(VkCommandBuffer command_buffer, lava::renderer& renderer, lava::image::ptr image, VkImageLayout image_layout, OnEncodeComplete function) override; @@ -90,6 +90,7 @@ public: void set_key_rate(uint32_t key_rate) override; void set_frame_rate(uint32_t frame_rate) override; + EncoderCodec get_codec() const override; EncoderMode get_mode() const override; double get_quality() const override; double get_bitrate() const override; diff --git a/src/headset/remote_headset.cpp b/src/headset/remote_headset.cpp index f7e4b94c158ac5c15a00fc8dcfbb22ec3ca93ec1..373f3e66d5967c8a06ce8a4e803f537911e969b1 100644 --- a/src/headset/remote_headset.cpp +++ b/src/headset/remote_headset.cpp @@ -493,10 +493,11 @@ bool RemoteHeadset::create_encoders() encoder->set_key_rate(this->encoder_key_rate); encoder->set_frame_rate(this->encoder_frame_rate); + EncoderCodec codec = this->get_application()->get_command_parser().get_codec(); lava::device_ptr device = this->get_application()->get_device(); lava::renderer& renderer = this->get_application()->get_renderer(); - if (!encoder->create(device, renderer, this->resolution, (EncoderFormat)this->get_format())) + if (!encoder->create(device, renderer, this->resolution, (EncoderFormat)this->get_format(), codec)) { return false; } @@ -511,11 +512,27 @@ bool RemoteHeadset::create_encoders() std::filesystem::create_directory(this->video_directory); } + EncoderCodec codec = this->get_application()->get_command_parser().get_codec(); + std::string file_extension; + + switch(codec) + { + case ENCODER_CODEC_H264: + file_extension = ".h264"; + break; + case ENCODER_CODEC_H265: + file_extension = ".h265"; + break; + default: + lava::log()->error("Invalid codec. Can't derive file extension!"); + return false; + } + this->encoder_files.resize(this->frame_id_count); for (uint32_t index = 0; index < this->encoder_files.size(); index++) { - std::string file_name = this->video_directory + "/" + build_file_name(index) + ".h264"; + std::string file_name = this->video_directory + "/" + build_file_name(index) + file_extension; this->encoder_files[index].open(file_name, std::ios::out | std::ios::binary); @@ -658,7 +675,9 @@ void RemoteHeadset::update_overlay() void RemoteHeadset::on_setup(const glm::u32vec2& resolution) { - this->transport->send_setup_complete(this->remote_strategy, this->frame_id_count, this->near_plane, this->far_plane); + EncoderCodec codec = this->get_application()->get_command_parser().get_codec(); + + this->transport->send_setup_complete(this->remote_strategy, codec, this->frame_id_count, this->near_plane, this->far_plane); std::unique_lock<std::mutex> lock(this->transport_mutex); this->resolution = resolution; diff --git a/src/transport/transport.hpp b/src/transport/transport.hpp index eedfcb859ec198cf77306934588bbcd7b108ef3e..580ed5d670b4494bf7e4291ad9790235e2421125 100644 --- a/src/transport/transport.hpp +++ b/src/transport/transport.hpp @@ -30,6 +30,7 @@ #include <span> #include "types.hpp" +#include "../encoder/encoder.hpp" enum TransportState { @@ -112,10 +113,11 @@ public: // send_setup_complete: Used to send a setup-complete packet // This function should be threadsafe and callable by main and worker thread // remote_strategy: The postprocessing method that should be used on the headset + // codec: The codec that is used for the encoding of the images. // max_frame_ids: The number of images that define a frame and that are send in parallel (normaly 2 for left and right eye). The headset application should setup this many decoders // near_plane: The distance to the near plane that should be used for the projection matrices // far_plane: The distance to the far plane that should be used for the projection matrices - virtual bool send_setup_complete(RemoteStrategy remote_strategy, uint32_t max_frame_ids, float near_plane, float far_plane) = 0; + virtual bool send_setup_complete(RemoteStrategy remote_strategy, EncoderCodec codec, uint32_t max_frame_ids, float near_plane, float far_plane) = 0; // send_stage_transform: Used to send the current transformation for the origin // This transformation can change for example if the user moves by pressing a controller button diff --git a/src/transport/udp_packet.cpp b/src/transport/udp_packet.cpp index 5500fae49c324f9248af84c179324e6e132d7d37..32f1346560ffd872685dcfcef49350deb3abd996 100644 --- a/src/transport/udp_packet.cpp +++ b/src/transport/udp_packet.cpp @@ -19,6 +19,24 @@ bool convert_udp_stereo_strategy(RemoteStrategy remote_strategey, UDPStereoStrat return true; } +bool convert_udp_codec(EncoderCodec codec, UDPCodec& udp_codec) +{ + switch(codec) + { + case ENCODER_CODEC_H264: + udp_codec = UDP_CODEC_H264; + break; + case ENCODER_CODEC_H265: + udp_codec = UDP_CODEC_H265; + break; + default: + lava::log()->error("UDP-Transport: Unkown coded!"); + return false; + } + + return true; +} + bool convert_udp_controller(UDPControllerId udp_controller, Controller& controller) { switch (udp_controller) diff --git a/src/transport/udp_packet.hpp b/src/transport/udp_packet.hpp index ab19362951827620eb646bb9897709cece83cf62..aa46ccdaeb83cdb475451f647647edbecb966cba 100644 --- a/src/transport/udp_packet.hpp +++ b/src/transport/udp_packet.hpp @@ -3,6 +3,7 @@ #include <cstdint> #include "types.hpp" +#include "../encoder/encoder.hpp" #define UDP_PACKET_PERFORMANCE_SAMPLE_NAME_MAX_LENGTH 128 #define UDP_PACKET_OVERLAY_TEXT_LABEL_MAX_LENGTH 128 @@ -42,6 +43,12 @@ enum UDPStereoStrategyId : uint32_t UDP_STEREO_STRATEGY_ID_MAX_COUNT }; +enum UDPCodec : uint32_t +{ + UDP_CODEC_H264 = 0x00, + UDP_CODEC_H265 = 0x01 +}; + enum UDPControllerId : uint32_t { UDP_CONTROLLER_ID_LEFT = 0x00, @@ -100,6 +107,7 @@ typedef uint32_t UDPFrameNumber; typedef uint32_t UDPTransformId; bool convert_udp_stereo_strategy(RemoteStrategy remote_strategey, UDPStereoStrategyId& udp_strategy); +bool convert_udp_codec(EncoderCodec codec, UDPCodec& udp_codec); bool convert_udp_controller(UDPControllerId udp_controller, Controller& controller); bool convert_udp_controller_state(UDPControllerState udp_controller_state, ControllerState& controller_state); bool convert_udp_button(UDPButtonId udp_button, Button& button); @@ -133,6 +141,7 @@ struct UDPPacketSetupComplete uint32_t size = 0; UDPStereoStrategyId strategy_id; + UDPCodec codec; uint32_t max_frame_ids; float near_plane; diff --git a/src/transport/udp_transport.cpp b/src/transport/udp_transport.cpp index 311101a55ee025b2f0960709ac85fe491ab34938..e10ac8139a191215173d8e6a19e3abde0a04551e 100644 --- a/src/transport/udp_transport.cpp +++ b/src/transport/udp_transport.cpp @@ -98,7 +98,7 @@ bool UDPTransport::wait_connect() return this->get_state() == TRANSPORT_STATE_CONNECTED; } -bool UDPTransport::send_setup_complete(RemoteStrategy remote_strategy, uint32_t max_frame_ids, float near_plane, float far_plane) +bool UDPTransport::send_setup_complete(RemoteStrategy remote_strategy, EncoderCodec codec, uint32_t max_frame_ids, float near_plane, float far_plane) { if (this->get_state() != TRANSPORT_STATE_CONNECTED) { @@ -107,13 +107,19 @@ bool UDPTransport::send_setup_complete(RemoteStrategy remote_strategy, uint32_t return false; } - UDPStereoStrategyId strategy_id; + UDPStereoStrategyId udp_strategy; - if (!convert_udp_stereo_strategy(remote_strategy, strategy_id)) + if (!convert_udp_stereo_strategy(remote_strategy, udp_strategy)) { return false; } + UDPCodec udp_codec; + + if (!convert_udp_codec(codec, udp_codec)) + { + return false; + } std::unique_lock<std::mutex> lock(this->worker_mutex); Datagram datagram = this->datagram_pool.acquire_datagram(); @@ -122,7 +128,8 @@ bool UDPTransport::send_setup_complete(RemoteStrategy remote_strategy, uint32_t UDPPacketSetupComplete* packet = (UDPPacketSetupComplete*)datagram.buffer; packet->id = UDP_PACKET_ID_SETUP_COMPLETE; packet->size = sizeof(UDPPacketSetupComplete); - packet->strategy_id = strategy_id; + packet->strategy_id = udp_strategy; + packet->codec = udp_codec; packet->max_frame_ids = max_frame_ids; packet->near_plane = near_plane; packet->far_plane = far_plane; diff --git a/src/transport/udp_transport.hpp b/src/transport/udp_transport.hpp index 4a80198c49d7c6cdf3b49307be3a4fd1797ad75a..5628fe137fc5157ec60eaf483a5a899eae950007 100644 --- a/src/transport/udp_transport.hpp +++ b/src/transport/udp_transport.hpp @@ -23,7 +23,7 @@ public: void destroy() override; bool wait_connect() override; - bool send_setup_complete(RemoteStrategy remote_strategy, uint32_t max_frame_ids, float near_plane, float far_plane) override; + bool send_setup_complete(RemoteStrategy remote_strategy, EncoderCodec codec, uint32_t max_frame_ids, float near_plane, float far_plane) override; bool send_stage_transform(const glm::mat4& stage_transform) override; bool send_frame_config_nal(FrameId frame_id, const std::span<const uint8_t>& content) override; bool send_frame_nal(FrameNumber frame_number, FrameId frame_id, TransformId transform_id, const std::span<const uint8_t>& content) override; diff --git a/src/utility/command_parser.cpp b/src/utility/command_parser.cpp index 77366016306e1e256fe5f6c1363ea7690299d741..5d134d371ed167b3ed7e0cc755f05ed419610dcd 100644 --- a/src/utility/command_parser.cpp +++ b/src/utility/command_parser.cpp @@ -116,6 +116,14 @@ bool CommandParser::parse_command(const argh::parser& cmd_line) } } + else if (parameter.first == "codec") + { + if(!this->set_codec(parameter.second)) + { + return false; + } + } + else if (parameter.first == "animation") { this->animation_name = parameter.second; @@ -242,6 +250,13 @@ bool CommandParser::parse_command(const argh::parser& cmd_line) } } + if (this->encoder == ENCODER_TYPE_VULKAN && this->codec == ENCODER_CODEC_H265) + { + std::cout << "Configuration 'encoder = vulkan' and 'codec = h265' not supported. Use option --help to get more information" << std::endl; + + return false; + } + return true; } @@ -265,6 +280,11 @@ EncoderType CommandParser::get_encoder() const return this->encoder; } +EncoderCodec CommandParser::get_codec() const +{ + return this->codec; +} + SceneUnit CommandParser::get_scene_unit() const { return this->scene_unit; @@ -355,6 +375,10 @@ void CommandParser::show_help() std::cout << " --encoder={encoder} The encoder that should be used when a remote headset is used." << std::endl; std::cout << " This parameter should only be used when the parameter headset is set to remote." << std::endl; std::cout << " Options: vulkan (default), nvidia" << std::endl; + std::cout << " --codec={codec} The codec that should be used by the encoder." << std::endl; + std::cout << " This parameter should only be used when the parameter headset is set to remote." << std::endl; + std::cout << " The vulkan encoder only supports h264 encoding." << std::endl; + std::cout << " Options: h264 (default), h265" << std::endl; std::cout << " --benchmark Play animation once and close program after completion." << std::endl; std::cout << " If not set, the application runs indefinitely and the interface is enabled." << std::endl; std::cout << " --animation={animation_name} The name of the animation that should be played." << std::endl; @@ -481,6 +505,28 @@ bool CommandParser::set_encoder(const std::string& name) return true; } +bool CommandParser::set_codec(const std::string& name) +{ + if (name == "h264") + { + this->codec = ENCODER_CODEC_H264; + } + + else if(name == "h265") + { + this->codec = ENCODER_CODEC_H265; + } + + else + { + std::cout << "Invalid option set of parameter 'codec'. Use option --help to get more information." << std::endl; + + return false; + } + + return true; +} + bool CommandParser::set_scene_unit(const std::string& name) { if (name == "meters") diff --git a/src/utility/command_parser.hpp b/src/utility/command_parser.hpp index 6507df74bccff6fc01a6092a1964f5b0b8c4e090..6a6df8a80839fe02f0502f628f757eb91065609d 100644 --- a/src/utility/command_parser.hpp +++ b/src/utility/command_parser.hpp @@ -25,6 +25,7 @@ public: StereoStrategyType get_stereo_strategy() const; TransportType get_transport() const; EncoderType get_encoder() const; + EncoderCodec get_codec() const; SceneUnit get_scene_unit() const; const std::string& get_scene_path() const; @@ -52,6 +53,7 @@ private: bool set_stero_strategy(const std::string& name); bool set_transport(const std::string& name); bool set_encoder(const std::string& name); + bool set_codec(const std::string& name); bool set_scene_unit(const std::string& name); private: @@ -59,6 +61,7 @@ private: StereoStrategyType stereo_strategy = STEREO_STRATEGY_TYPE_NATIVE_FORWARD; TransportType transport = TRANSPORT_TYPE_UDP; EncoderType encoder = ENCODER_TYPE_VULKAN; + EncoderCodec codec = ENCODER_CODEC_H264; SceneUnit scene_unit = SCENE_UNIT_METERS; std::string scene_path = "";