1 /* 2 * Modern effects for a modern Streamer 3 * Copyright (C) 2020 Michael Fabian Dirks 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #pragma once 21 #include "common.hpp" 22 #include "plugin.hpp" 23 24 namespace obs { 25 template<class _factory, typename _instance> 26 class encoder_factory { 27 public: 28 typedef _factory factory_t; 29 typedef _instance instance_t; 30 31 protected: 32 obs_encoder_info _info = {}; 33 obs_encoder_info _info_fallback = {}; 34 std::string _info_fallback_id; 35 36 std::map<std::string, std::shared_ptr<obs_encoder_info>> _proxies; 37 std::set<std::string> _proxy_names; 38 39 public: encoder_factory()40 encoder_factory() 41 { 42 _info.type_data = this; 43 44 _info.get_name = _get_name; 45 _info.create = _create_hw; 46 _info.destroy = _destroy; 47 _info.get_defaults2 = _get_defaults2; 48 _info.get_properties2 = _get_properties2; 49 _info.update = _update; 50 _info.encode = _encode; 51 _info.get_extra_data = _get_extra_data; 52 _info.get_sei_data = _get_sei_data; 53 } ~encoder_factory()54 virtual ~encoder_factory() {} 55 finish_setup()56 void finish_setup() 57 { 58 if (_info.type == OBS_ENCODER_AUDIO) { 59 _info.get_frame_size = _get_frame_size; 60 _info.get_audio_info = _get_audio_info; 61 } else if (_info.type == OBS_ENCODER_VIDEO) { 62 _info.get_video_info = _get_video_info; 63 } 64 if (_info.caps & OBS_ENCODER_CAP_PASS_TEXTURE) { 65 _info.encode_texture = _encode_texture; 66 67 memcpy(&_info_fallback, &_info, sizeof(obs_encoder_info)); 68 _info_fallback_id = std::string(_info.id) + "_sw"; 69 _info_fallback.id = _info_fallback_id.c_str(); 70 _info_fallback.caps = 71 (_info_fallback.caps & ~OBS_ENCODER_CAP_PASS_TEXTURE) | OBS_ENCODER_CAP_DEPRECATED; 72 _info_fallback.create = _create; 73 _info_fallback.encode_texture = nullptr; 74 obs_register_encoder(&_info_fallback); 75 } else { 76 _info.create = _create; 77 } 78 79 obs_register_encoder(&_info); 80 } 81 register_proxy(std::string_view name)82 void register_proxy(std::string_view name) 83 { 84 auto iter = _proxy_names.emplace(name); 85 86 // Create proxy. 87 std::shared_ptr<obs_encoder_info> proxy = std::make_shared<obs_encoder_info>(); 88 memcpy(proxy.get(), &_info, sizeof(obs_encoder_info)); 89 proxy->id = iter.first->c_str(); 90 proxy->caps |= OBS_ENCODER_CAP_DEPRECATED; 91 obs_register_encoder(proxy.get()); 92 93 _proxies.emplace(name, proxy); 94 } 95 96 private /* Factory */: _get_name(void * type_data)97 static const char* _get_name(void* type_data) noexcept 98 try { 99 if (type_data) 100 return reinterpret_cast<factory_t*>(type_data)->get_name(); 101 return nullptr; 102 } catch (const std::exception& ex) { 103 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 104 return nullptr; 105 } catch (...) { 106 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 107 return nullptr; 108 } 109 _create(obs_data_t * settings,obs_encoder_t * encoder)110 static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept 111 try { 112 auto* fac = reinterpret_cast<factory_t*>(obs_encoder_get_type_data(encoder)); 113 return fac->create(settings, encoder, false); 114 } catch (const std::exception& ex) { 115 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 116 return nullptr; 117 } catch (...) { 118 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 119 return nullptr; 120 } 121 _create_hw(obs_data_t * settings,obs_encoder_t * encoder)122 static void* _create_hw(obs_data_t* settings, obs_encoder_t* encoder) noexcept 123 try { 124 auto* fac = reinterpret_cast<factory_t*>(obs_encoder_get_type_data(encoder)); 125 try { 126 return fac->create(settings, encoder, true); 127 } catch (...) { 128 return obs_encoder_create_rerouted(encoder, fac->_info_fallback.id); 129 } 130 } catch (const std::exception& ex) { 131 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 132 return nullptr; 133 } catch (...) { 134 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 135 return nullptr; 136 } 137 _get_defaults2(obs_data_t * settings,void * type_data)138 static void _get_defaults2(obs_data_t* settings, void* type_data) noexcept 139 try { 140 if (type_data) 141 reinterpret_cast<factory_t*>(type_data)->get_defaults2(settings); 142 } catch (const std::exception& ex) { 143 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 144 } catch (...) { 145 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 146 } 147 _get_properties2(void * data,void * type_data)148 static obs_properties_t* _get_properties2(void* data, void* type_data) noexcept 149 try { 150 if (type_data) 151 return reinterpret_cast<factory_t*>(type_data)->get_properties2(reinterpret_cast<instance_t*>(data)); 152 return nullptr; 153 } catch (const std::exception& ex) { 154 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 155 return nullptr; 156 } catch (...) { 157 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 158 return nullptr; 159 } 160 161 private /* Instance */: _destroy(void * data)162 static void _destroy(void* data) noexcept 163 try { 164 if (data) 165 delete reinterpret_cast<instance_t*>(data); 166 } catch (const std::exception& ex) { 167 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 168 } catch (...) { 169 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 170 } 171 _update(void * data,obs_data_t * settings)172 static bool _update(void* data, obs_data_t* settings) noexcept 173 try { 174 auto priv = reinterpret_cast<instance_t*>(data); 175 if (priv) { 176 uint64_t version = static_cast<uint64_t>(obs_data_get_int(settings, S_VERSION)); 177 priv->migrate(settings, version); 178 obs_data_set_int(settings, S_VERSION, static_cast<int64_t>(STREAMFX_VERSION)); 179 obs_data_set_string(settings, S_COMMIT, STREAMFX_COMMIT); 180 return priv->update(settings); 181 } 182 return false; 183 } catch (const std::exception& ex) { 184 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 185 return false; 186 } catch (...) { 187 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 188 return false; 189 } 190 _encode(void * data,struct encoder_frame * frame,struct encoder_packet * packet,bool * received_packet)191 static bool _encode(void* data, struct encoder_frame* frame, struct encoder_packet* packet, 192 bool* received_packet) noexcept 193 try { 194 if (data) 195 return reinterpret_cast<instance_t*>(data)->encode(frame, packet, received_packet); 196 return false; 197 } catch (const std::exception& ex) { 198 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 199 return false; 200 } catch (...) { 201 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 202 return false; 203 } 204 _encode_texture(void * data,uint32_t handle,int64_t pts,uint64_t lock_key,uint64_t * next_key,struct encoder_packet * packet,bool * received_packet)205 static bool _encode_texture(void* data, uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key, 206 struct encoder_packet* packet, bool* received_packet) noexcept 207 try { 208 if (data) 209 return reinterpret_cast<instance_t*>(data)->encode_video(handle, pts, lock_key, next_key, packet, 210 received_packet); 211 return false; 212 } catch (const std::exception& ex) { 213 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 214 return false; 215 } catch (...) { 216 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 217 return false; 218 } 219 _get_frame_size(void * data)220 static size_t _get_frame_size(void* data) noexcept 221 try { 222 if (data) 223 return reinterpret_cast<instance_t*>(data)->get_frame_size(); 224 return 0; 225 } catch (const std::exception& ex) { 226 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 227 return 0; 228 } catch (...) { 229 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 230 return 0; 231 } 232 _get_extra_data(void * data,uint8_t ** extra_data,size_t * size)233 static bool _get_extra_data(void* data, uint8_t** extra_data, size_t* size) noexcept 234 try { 235 if (data) 236 return reinterpret_cast<instance_t*>(data)->get_extra_data(extra_data, size); 237 return false; 238 } catch (const std::exception& ex) { 239 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 240 return false; 241 } catch (...) { 242 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 243 return false; 244 } 245 _get_sei_data(void * data,uint8_t ** sei_data,size_t * size)246 static bool _get_sei_data(void* data, uint8_t** sei_data, size_t* size) noexcept 247 try { 248 if (data) 249 return reinterpret_cast<instance_t*>(data)->get_sei_data(sei_data, size); 250 return false; 251 } catch (const std::exception& ex) { 252 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 253 return false; 254 } catch (...) { 255 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 256 return false; 257 } 258 _get_audio_info(void * data,struct audio_convert_info * info)259 static void _get_audio_info(void* data, struct audio_convert_info* info) noexcept 260 try { 261 if (data) 262 reinterpret_cast<instance_t*>(data)->get_audio_info(info); 263 } catch (const std::exception& ex) { 264 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 265 } catch (...) { 266 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 267 } 268 _get_video_info(void * data,struct video_scale_info * info)269 static void _get_video_info(void* data, struct video_scale_info* info) noexcept 270 try { 271 if (data) 272 reinterpret_cast<instance_t*>(data)->get_video_info(info); 273 } catch (const std::exception& ex) { 274 DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what()); 275 } catch (...) { 276 DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 277 } 278 279 public: get_name()280 virtual const char* get_name() 281 { 282 return "Not Yet Implemented"; 283 } 284 create(obs_data_t * settings,obs_encoder_t * encoder,bool is_hw)285 virtual void* create(obs_data_t* settings, obs_encoder_t* encoder, bool is_hw) 286 { 287 return reinterpret_cast<void*>(new instance_t(settings, encoder, is_hw)); 288 } 289 get_defaults2(obs_data_t * data)290 virtual void get_defaults2(obs_data_t* data) {} 291 get_properties2(instance_t * data)292 virtual obs_properties_t* get_properties2(instance_t* data) 293 { 294 return nullptr; 295 } 296 }; 297 298 class encoder_instance { 299 protected: 300 obs_encoder_t* _self; 301 302 public: encoder_instance(obs_data_t * settings,obs_encoder_t * self,bool is_hw)303 encoder_instance(obs_data_t* settings, obs_encoder_t* self, bool is_hw) : _self(self) {} ~encoder_instance()304 virtual ~encoder_instance(){}; 305 migrate(obs_data_t * settings,uint64_t version)306 virtual void migrate(obs_data_t* settings, uint64_t version) {} 307 update(obs_data_t * settings)308 virtual bool update(obs_data_t* settings) 309 { 310 return false; 311 } 312 encode(struct encoder_frame * frame,struct encoder_packet * packet,bool * received_packet)313 virtual bool encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet) 314 { 315 auto type = obs_encoder_get_type(_self); 316 if (type == OBS_ENCODER_VIDEO) { 317 return encode_video(frame, packet, received_packet); 318 } else if (type == OBS_ENCODER_AUDIO) { 319 return encode_audio(frame, packet, received_packet); 320 } 321 return false; 322 } 323 324 virtual bool encode_audio(struct encoder_frame* frame, struct encoder_packet* packet, 325 bool* received_packet) = 0; 326 327 virtual bool encode_video(struct encoder_frame* frame, struct encoder_packet* packet, 328 bool* received_packet) = 0; 329 330 virtual bool encode_video(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key, 331 struct encoder_packet* packet, bool* received_packet) = 0; 332 get_frame_size()333 virtual size_t get_frame_size() 334 { 335 return 0; 336 } 337 get_extra_data(uint8_t ** extra_data,size_t * size)338 virtual bool get_extra_data(uint8_t** extra_data, size_t* size) 339 { 340 return false; 341 } 342 get_sei_data(uint8_t ** sei_data,size_t * size)343 virtual bool get_sei_data(uint8_t** sei_data, size_t* size) 344 { 345 return false; 346 } 347 get_audio_info(struct audio_convert_info * info)348 virtual void get_audio_info(struct audio_convert_info* info) {} 349 get_video_info(struct video_scale_info * info)350 virtual void get_video_info(struct video_scale_info* info) {} 351 }; 352 353 } // namespace obs 354