xref: /haiku/src/kits/media/PluginManager.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
1 /*
2  * Copyright 2004-2010, Marcus Overhagen. All rights reserved.
3  * Distributed under the terms of the OpenBeOS License.
4  */
5 
6 #include <Path.h>
7 #include <image.h>
8 #include <string.h>
9 
10 #include "PluginManager.h"
11 #include "DataExchange.h"
12 #include "debug.h"
13 
14 
15 PluginManager gPluginManager;
16 
17 
18 // #pragma mark - Readers/Decoders
19 
20 
21 status_t
22 PluginManager::CreateReader(Reader** reader, int32* streamCount,
23 	media_file_format* mff, BDataIO* source)
24 {
25 	TRACE("PluginManager::CreateReader enter\n");
26 
27 	BPositionIO *seekable_source = dynamic_cast<BPositionIO *>(source);
28 	if (seekable_source == 0) {
29 		printf("PluginManager::CreateReader: non-seekable sources not "
30 			"supported yet\n");
31 		return B_ERROR;
32 	}
33 
34 	// get list of available readers from the server
35 	server_get_readers_request request;
36 	server_get_readers_reply reply;
37 	status_t ret = QueryServer(SERVER_GET_READERS, &request, sizeof(request),
38 		&reply, sizeof(reply));
39 	if (ret != B_OK) {
40 		printf("PluginManager::CreateReader: can't get list of readers: %s\n",
41 			strerror(ret));
42 		return ret;
43 	}
44 
45 	// try each reader by calling it's Sniff function...
46 	for (int32 i = 0; i < reply.count; i++) {
47 		entry_ref ref = reply.ref[i];
48 		MediaPlugin* plugin = GetPlugin(ref);
49 		if (plugin == NULL) {
50 			printf("PluginManager::CreateReader: GetPlugin failed\n");
51 			return B_ERROR;
52 		}
53 
54 		ReaderPlugin* readerPlugin = dynamic_cast<ReaderPlugin*>(plugin);
55 		if (readerPlugin == NULL) {
56 			printf("PluginManager::CreateReader: dynamic_cast failed\n");
57 			PutPlugin(plugin);
58 			return B_ERROR;
59 		}
60 
61 		*reader = readerPlugin->NewReader();
62 		if (*reader == NULL) {
63 			printf("PluginManager::CreateReader: NewReader failed\n");
64 			PutPlugin(plugin);
65 			return B_ERROR;
66 		}
67 
68 		seekable_source->Seek(0, SEEK_SET);
69 		(*reader)->Setup(seekable_source);
70 		(*reader)->fMediaPlugin = plugin;
71 
72 		if ((*reader)->Sniff(streamCount) == B_OK) {
73 			TRACE("PluginManager::CreateReader: Sniff success "
74 				"(%ld stream(s))\n", *streamCount);
75 			(*reader)->GetFileFormatInfo(mff);
76 			return B_OK;
77 		}
78 
79 		DestroyReader(*reader);
80 		*reader = NULL;
81 	}
82 
83 	TRACE("PluginManager::CreateReader leave\n");
84 	return B_MEDIA_NO_HANDLER;
85 }
86 
87 
88 void
89 PluginManager::DestroyReader(Reader* reader)
90 {
91 	if (reader != NULL) {
92 		TRACE("PluginManager::DestroyReader(%p (plugin: %p))\n", reader,
93 			reader->fMediaPlugin);
94 		// NOTE: We have to put the plug-in after deleting the reader,
95 		// since otherwise we may actually unload the code for the
96 		// destructor...
97 		MediaPlugin* plugin = reader->fMediaPlugin;
98 		delete reader;
99 		PutPlugin(plugin);
100 	}
101 }
102 
103 
104 status_t
105 PluginManager::CreateDecoder(Decoder** _decoder, const media_format& format)
106 {
107 	TRACE("PluginManager::CreateDecoder enter\n");
108 
109 	// get decoder for this format from the server
110 	server_get_decoder_for_format_request request;
111 	server_get_decoder_for_format_reply reply;
112 	request.format = format;
113 	status_t ret = QueryServer(SERVER_GET_DECODER_FOR_FORMAT, &request,
114 		sizeof(request), &reply, sizeof(reply));
115 	if (ret != B_OK) {
116 		printf("PluginManager::CreateDecoder: can't get decoder for format: "
117 			"%s\n", strerror(ret));
118 		return ret;
119 	}
120 
121 	MediaPlugin* plugin = GetPlugin(reply.ref);
122 	if (plugin == NULL) {
123 		printf("PluginManager::CreateDecoder: GetPlugin failed\n");
124 		return B_ERROR;
125 	}
126 
127 	DecoderPlugin* decoderPlugin = dynamic_cast<DecoderPlugin*>(plugin);
128 	if (decoderPlugin == NULL) {
129 		printf("PluginManager::CreateDecoder: dynamic_cast failed\n");
130 		PutPlugin(plugin);
131 		return B_ERROR;
132 	}
133 
134 	// TODO: In theory, one DecoderPlugin could support multiple Decoders,
135 	// but this is not yet handled (passing "0" as index/ID).
136 	*_decoder = decoderPlugin->NewDecoder(0);
137 	if (*_decoder == NULL) {
138 		printf("PluginManager::CreateDecoder: NewDecoder() failed\n");
139 		PutPlugin(plugin);
140 		return B_ERROR;
141 	}
142 	TRACE("  created decoder: %p\n", *_decoder);
143 	(*_decoder)->fMediaPlugin = plugin;
144 
145 	TRACE("PluginManager::CreateDecoder leave\n");
146 
147 	return B_OK;
148 }
149 
150 
151 status_t
152 PluginManager::CreateDecoder(Decoder** decoder, const media_codec_info& mci)
153 {
154 	// TODO
155 	debugger("not implemented");
156 	return B_ERROR;
157 }
158 
159 
160 status_t
161 PluginManager::GetDecoderInfo(Decoder* decoder, media_codec_info* _info) const
162 {
163 	if (decoder == NULL)
164 		return B_BAD_VALUE;
165 
166 	decoder->GetCodecInfo(_info);
167 	// TODO:
168 	// out_info->id =
169 	// out_info->sub_id =
170 	return B_OK;
171 }
172 
173 
174 void
175 PluginManager::DestroyDecoder(Decoder* decoder)
176 {
177 	if (decoder != NULL) {
178 		TRACE("PluginManager::DestroyDecoder(%p, plugin: %p)\n", decoder,
179 			decoder->fMediaPlugin);
180 		// NOTE: We have to put the plug-in after deleting the decoder,
181 		// since otherwise we may actually unload the code for the
182 		// destructor...
183 		MediaPlugin* plugin = decoder->fMediaPlugin;
184 		delete decoder;
185 		PutPlugin(plugin);
186 	}
187 }
188 
189 
190 // #pragma mark - Writers/Encoders
191 
192 
193 status_t
194 PluginManager::CreateWriter(Writer** writer, const media_file_format& mff,
195 	BDataIO* target)
196 {
197 	TRACE("PluginManager::CreateWriter enter\n");
198 
199 	// Get the Writer responsible for this media_file_format from the server.
200 	server_get_writer_request request;
201 	request.internal_id = mff.id.internal_id;
202 	server_get_writer_reply reply;
203 	status_t ret = QueryServer(SERVER_GET_WRITER_FOR_FORMAT_FAMILY, &request,
204 		sizeof(request), &reply, sizeof(reply));
205 	if (ret != B_OK) {
206 		printf("PluginManager::CreateWriter: can't get writer for file "
207 			"family: %s\n", strerror(ret));
208 		return ret;
209 	}
210 
211 	MediaPlugin* plugin = GetPlugin(reply.ref);
212 	if (plugin == NULL) {
213 		printf("PluginManager::CreateWriter: GetPlugin failed\n");
214 		return B_ERROR;
215 	}
216 
217 	WriterPlugin* writerPlugin = dynamic_cast<WriterPlugin*>(plugin);
218 	if (writerPlugin == NULL) {
219 		printf("PluginManager::CreateWriter: dynamic_cast failed\n");
220 		PutPlugin(plugin);
221 		return B_ERROR;
222 	}
223 
224 	*writer = writerPlugin->NewWriter();
225 	if (*writer == NULL) {
226 		printf("PluginManager::CreateWriter: NewWriter failed\n");
227 		PutPlugin(plugin);
228 		return B_ERROR;
229 	}
230 
231 	(*writer)->Setup(target);
232 	(*writer)->fMediaPlugin = plugin;
233 
234 	TRACE("PluginManager::CreateWriter leave\n");
235 	return B_OK;
236 }
237 
238 
239 void
240 PluginManager::DestroyWriter(Writer* writer)
241 {
242 	if (writer != NULL) {
243 		TRACE("PluginManager::DestroyWriter(%p (plugin: %p))\n", writer,
244 			reader->fMediaPlugin);
245 		// NOTE: We have to put the plug-in after deleting the writer,
246 		// since otherwise we may actually unload the code for the
247 		// destructor...
248 		MediaPlugin* plugin = writer->fMediaPlugin;
249 		delete writer;
250 		PutPlugin(plugin);
251 	}
252 }
253 
254 
255 status_t
256 PluginManager::CreateEncoder(Encoder** _encoder,
257 	const media_codec_info* codecInfo, uint32 flags)
258 {
259 	TRACE("PluginManager::CreateEncoder enter\n");
260 
261 	// Get encoder for this codec info from the server
262 	server_get_encoder_for_codec_info_request request;
263 	server_get_encoder_for_codec_info_reply reply;
264 	request.id = codecInfo->id;
265 	status_t ret = QueryServer(SERVER_GET_ENCODER_FOR_CODEC_INFO, &request,
266 		sizeof(request), &reply, sizeof(reply));
267 	if (ret != B_OK) {
268 		printf("PluginManager::CreateEncoder: can't get encoder for codec %s: "
269 			"%s\n", codecInfo->pretty_name, strerror(ret));
270 		return ret;
271 	}
272 
273 	MediaPlugin* plugin = GetPlugin(reply.ref);
274 	if (!plugin) {
275 		printf("PluginManager::CreateEncoder: GetPlugin failed\n");
276 		return B_ERROR;
277 	}
278 
279 	EncoderPlugin* encoderPlugin = dynamic_cast<EncoderPlugin*>(plugin);
280 	if (encoderPlugin == NULL) {
281 		printf("PluginManager::CreateEncoder: dynamic_cast failed\n");
282 		PutPlugin(plugin);
283 		return B_ERROR;
284 	}
285 
286 	*_encoder = encoderPlugin->NewEncoder(*codecInfo);
287 	if (*_encoder == NULL) {
288 		printf("PluginManager::CreateEncoder: NewEncoder() failed\n");
289 		PutPlugin(plugin);
290 		return B_ERROR;
291 	}
292 	TRACE("  created encoder: %p\n", *_encoder);
293 	(*_encoder)->fMediaPlugin = plugin;
294 
295 	TRACE("PluginManager::CreateEncoder leave\n");
296 
297 	return B_OK;
298 }
299 
300 
301 void
302 PluginManager::DestroyEncoder(Encoder* encoder)
303 {
304 	if (encoder != NULL) {
305 		TRACE("PluginManager::DestroyEncoder(%p, plugin: %p)\n", encoder,
306 			encoder->fMediaPlugin);
307 		// NOTE: We have to put the plug-in after deleting the encoder,
308 		// since otherwise we may actually unload the code for the
309 		// destructor...
310 		MediaPlugin* plugin = encoder->fMediaPlugin;
311 		delete encoder;
312 		PutPlugin(plugin);
313 	}
314 }
315 
316 
317 // #pragma mark -
318 
319 
320 PluginManager::PluginManager()
321 	:
322 	fPluginList(),
323 	fLocker("media plugin manager")
324 {
325 	CALLED();
326 }
327 
328 
329 PluginManager::~PluginManager()
330 {
331 	CALLED();
332 	for (int i = fPluginList.CountItems() - 1; i >= 0; i--) {
333 		plugin_info* info = NULL;
334 		fPluginList.Get(i, &info);
335 		printf("PluginManager: Error, unloading PlugIn %s with usecount "
336 			"%d\n", info->name, info->usecount);
337 		delete info->plugin;
338 		unload_add_on(info->image);
339 	}
340 }
341 
342 
343 MediaPlugin*
344 PluginManager::GetPlugin(const entry_ref& ref)
345 {
346 	TRACE("PluginManager::GetPlugin(%s)\n", ref.name);
347 	fLocker.Lock();
348 
349 	MediaPlugin* plugin;
350 	plugin_info* pinfo;
351 	plugin_info info;
352 
353 	for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) {
354 		if (0 == strcmp(ref.name, pinfo->name)) {
355 			plugin = pinfo->plugin;
356 			pinfo->usecount++;
357 			TRACE("  found existing plugin: %p\n", pinfo->plugin);
358 			fLocker.Unlock();
359 			return plugin;
360 		}
361 	}
362 
363 	if (_LoadPlugin(ref, &info.plugin, &info.image) < B_OK) {
364 		printf("PluginManager: Error, loading PlugIn %s failed\n", ref.name);
365 		fLocker.Unlock();
366 		return NULL;
367 	}
368 
369 	strcpy(info.name, ref.name);
370 	info.usecount = 1;
371 	fPluginList.Insert(info);
372 
373 	TRACE("PluginManager: PlugIn %s loaded\n", ref.name);
374 
375 	plugin = info.plugin;
376 	TRACE("  loaded plugin: %p\n", plugin);
377 
378 	fLocker.Unlock();
379 	return plugin;
380 }
381 
382 
383 void
384 PluginManager::PutPlugin(MediaPlugin* plugin)
385 {
386 	TRACE("PluginManager::PutPlugin()\n");
387 	fLocker.Lock();
388 
389 	plugin_info* pinfo;
390 
391 	for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) {
392 		if (plugin == pinfo->plugin) {
393 			pinfo->usecount--;
394 			if (pinfo->usecount == 0) {
395 				TRACE("  deleting %p\n", pinfo->plugin);
396 				delete pinfo->plugin;
397 				TRACE("  unloading add-on: %ld\n\n", pinfo->image);
398 				unload_add_on(pinfo->image);
399 				fPluginList.RemoveCurrent();
400 			}
401 			fLocker.Unlock();
402 			return;
403 		}
404 	}
405 
406 	printf("PluginManager: Error, can't put PlugIn %p\n", plugin);
407 
408 	fLocker.Unlock();
409 }
410 
411 
412 status_t
413 PluginManager::_LoadPlugin(const entry_ref& ref, MediaPlugin** plugin,
414 	image_id* image)
415 {
416 	BPath p(&ref);
417 
418 	TRACE("PluginManager: _LoadPlugin trying to load %s\n", p.Path());
419 
420 	image_id id;
421 	id = load_add_on(p.Path());
422 	if (id < 0) {
423 		printf("PluginManager: Error, load_add_on(): %s\n", strerror(id));
424 		return B_ERROR;
425 	}
426 
427 	MediaPlugin* (*instantiate_plugin_func)();
428 
429 	if (get_image_symbol(id, "instantiate_plugin", B_SYMBOL_TYPE_TEXT,
430 			(void**)&instantiate_plugin_func) < B_OK) {
431 		printf("PluginManager: Error, _LoadPlugin can't find "
432 			"instantiate_plugin in %s\n", p.Path());
433 		unload_add_on(id);
434 		return B_ERROR;
435 	}
436 
437 	MediaPlugin *pl;
438 
439 	pl = (*instantiate_plugin_func)();
440 	if (pl == NULL) {
441 		printf("PluginManager: Error, _LoadPlugin instantiate_plugin in %s "
442 			"returned NULL\n", p.Path());
443 		unload_add_on(id);
444 		return B_ERROR;
445 	}
446 
447 	*plugin = pl;
448 	*image = id;
449 	return B_OK;
450 }
451