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