⛏️ index : haiku.git

/* 
 * Copyright 2004-2010, Marcus Overhagen. All rights reserved.
 * Copyright 2016, Dario Casalinuovo. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include <AdapterIO.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <BufferIO.h>
#include <DataIO.h>
#include <image.h>
#include <Path.h>

#include <string.h>

#include "AddOnManager.h"
#include "PluginManager.h"
#include "DataExchange.h"
#include "MediaDebug.h"


PluginManager gPluginManager;

#define BLOCK_SIZE 4096
#define MAX_STREAMERS 40


class DataIOAdapter : public BAdapterIO {
public:
	DataIOAdapter(BDataIO* dataIO)
		:
		BAdapterIO(B_MEDIA_SEEK_BACKWARD | B_MEDIA_MUTABLE_SIZE,
			B_INFINITE_TIMEOUT),
		fDataIO(dataIO)
	{
		fDataInputAdapter = BuildInputAdapter();
	}

	virtual	~DataIOAdapter()
	{
	}

	virtual	ssize_t	ReadAt(off_t position, void* buffer,
		size_t size)
	{
		if (position == Position()) {
			ssize_t ret = fDataIO->Read(buffer, size);
			fDataInputAdapter->Write(buffer, ret);
			return ret;
		}

		off_t totalSize = 0;
		if (GetSize(&totalSize) != B_OK)
			return B_UNSUPPORTED;

		if (position+size < (size_t)totalSize)
			return ReadAt(position, buffer, size);

		return B_NOT_SUPPORTED;
	}

	virtual	ssize_t	WriteAt(off_t position, const void* buffer,
		size_t size)
	{
		if (position == Position()) {
			ssize_t ret = fDataIO->Write(buffer, size);
			fDataInputAdapter->Write(buffer, ret);
			return ret;
		}

		return B_NOT_SUPPORTED;
	}

private:
	BDataIO*		fDataIO;
	BInputAdapter*	fDataInputAdapter;
};


class BMediaIOWrapper : public BMediaIO {
public:
	BMediaIOWrapper(BDataIO* source)
		:
		fData(NULL),
		fPosition(NULL),
		fMedia(NULL),
		fDataIOAdapter(NULL),
		fErr(B_NO_ERROR)
	{
		CALLED();

		fPosition = dynamic_cast<BPositionIO*>(source);
		fMedia = dynamic_cast<BMediaIO*>(source);
		fData = source;

		if (!IsPosition()) {
			// In this case we have to supply our own form
			// of pseudo-seekable object from a non-seekable
			// BDataIO.
			fDataIOAdapter = new DataIOAdapter(source);
			fMedia = dynamic_cast<BMediaIO*>(fDataIOAdapter);
			fPosition = dynamic_cast<BPositionIO*>(fDataIOAdapter);
			fData = dynamic_cast<BDataIO*>(fDataIOAdapter);
			TRACE("Unable to improve performance with a BufferIO\n");
		}

		if (IsMedia())
			fMedia->GetFlags(&fFlags);
		else if (IsPosition())
			fFlags = B_MEDIA_SEEKABLE;
	}

	virtual	~BMediaIOWrapper()
	{
		if (fDataIOAdapter != NULL)
			delete fDataIOAdapter;
	}

	status_t InitCheck() const
	{
		return fErr;
	}

	// BMediaIO interface

	virtual void GetFlags(int32* flags) const
	{
		*flags = fFlags;
	}

	// BPositionIO interface

	virtual	ssize_t ReadAt(off_t position, void* buffer,
		size_t size)
	{
		CALLED();

		return fPosition->ReadAt(position, buffer, size);
	}

	virtual	ssize_t WriteAt(off_t position, const void* buffer,
		size_t size)
	{
		CALLED();

		return fPosition->WriteAt(position, buffer, size);
	}

	virtual	off_t Seek(off_t position, uint32 seekMode)
	{
		CALLED();

		return fPosition->Seek(position, seekMode);

	}

	virtual off_t Position() const
	{
		CALLED();

		return fPosition->Position();
	}

	virtual	status_t SetSize(off_t size)
	{
		CALLED();

		return fPosition->SetSize(size);
	}

	virtual	status_t GetSize(off_t* size) const
	{
		CALLED();

		return fPosition->GetSize(size);
	}

protected:

	bool IsMedia() const
	{
		return fMedia != NULL;
	}

	bool IsPosition() const
	{
		return fPosition != NULL;
	}

private:
	BDataIO*			fData;
	BPositionIO*		fPosition;
	BMediaIO*			fMedia;
	DataIOAdapter*		fDataIOAdapter;

	int32				fFlags;

	status_t			fErr;
};


// #pragma mark - Readers/Decoders


status_t
PluginManager::CreateReader(Reader** reader, int32* streamCount,
	media_file_format* mff, BDataIO* source)
{
	TRACE("PluginManager::CreateReader enter\n");

	// The wrapper class will present our source in a more useful
	// way, we create an instance which is buffering our reads and
	// writes.
	BMediaIOWrapper* buffered_source = new BMediaIOWrapper(source);
	ObjectDeleter<BMediaIOWrapper> ioDeleter(buffered_source);

	status_t ret = buffered_source->InitCheck();
	if (ret != B_OK)
		return ret;

	// get list of available readers from the server
	entry_ref refs[MAX_READERS];
	int32 count;

	ret = AddOnManager::GetInstance()->GetReaders(refs, &count,
		MAX_READERS);
	if (ret != B_OK) {
		printf("PluginManager::CreateReader: can't get list of readers: %s\n",
			strerror(ret));
		return ret;
	}

	// try each reader by calling it's Sniff function...
	for (int32 i = 0; i < count; i++) {
		const entry_ref& ref = refs[i];
		MediaPlugin* plugin = GetPlugin(ref);
		if (plugin == NULL) {
			printf("PluginManager::CreateReader: GetPlugin failed\n");
			return B_ERROR;
		}

		ReaderPlugin* readerPlugin = dynamic_cast<ReaderPlugin*>(plugin);
		if (readerPlugin == NULL) {
			printf("PluginManager::CreateReader: dynamic_cast failed\n");
			PutPlugin(plugin);
			return B_ERROR;
		}

		*reader = readerPlugin->NewReader();
		if (*reader == NULL) {
			printf("PluginManager::CreateReader: NewReader failed\n");
			PutPlugin(plugin);
			return B_ERROR;
		}

		buffered_source->Seek(0, SEEK_SET);
		(*reader)->Setup(buffered_source);
		(*reader)->fMediaPlugin = plugin;

		if ((*reader)->Sniff(streamCount) == B_OK) {
			TRACE("PluginManager::CreateReader: Sniff success "
				"(%" B_PRId32 " stream(s))\n", *streamCount);
			(*reader)->GetFileFormatInfo(mff);
			ioDeleter.Detach();
			return B_OK;
		}

		DestroyReader(*reader);
		*reader = NULL;
	}

	TRACE("PluginManager::CreateReader leave\n");
	return B_MEDIA_NO_HANDLER;
}


void
PluginManager::DestroyReader(Reader* reader)
{
	if (reader != NULL) {
		TRACE("PluginManager::DestroyReader(%p (plugin: %p))\n", reader,
			reader->fMediaPlugin);
		// NOTE: We have to put the plug-in after deleting the reader,
		// since otherwise we may actually unload the code for the
		// destructor...
		MediaPlugin* plugin = reader->fMediaPlugin;
		delete reader;
		PutPlugin(plugin);
	}
}


status_t
PluginManager::CreateDecoder(Decoder** _decoder, const media_format& format)
{
	TRACE("PluginManager::CreateDecoder enter\n");

	// get decoder for this format
	entry_ref ref;
	status_t ret = AddOnManager::GetInstance()->GetDecoderForFormat(
		&ref, format);
	if (ret != B_OK) {
		printf("PluginManager::CreateDecoder: can't get decoder for format: "
			"%s\n", strerror(ret));
		return ret;
	}

	MediaPlugin* plugin = GetPlugin(ref);
	if (plugin == NULL) {
		printf("PluginManager::CreateDecoder: GetPlugin failed\n");
		return B_ERROR;
	}
	
	DecoderPlugin* decoderPlugin = dynamic_cast<DecoderPlugin*>(plugin);
	if (decoderPlugin == NULL) {
		printf("PluginManager::CreateDecoder: dynamic_cast failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}
	
	// TODO: In theory, one DecoderPlugin could support multiple Decoders,
	// but this is not yet handled (passing "0" as index/ID).
	*_decoder = decoderPlugin->NewDecoder(0);
	if (*_decoder == NULL) {
		printf("PluginManager::CreateDecoder: NewDecoder() failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}
	TRACE("  created decoder: %p\n", *_decoder);
	(*_decoder)->fMediaPlugin = plugin;

	TRACE("PluginManager::CreateDecoder leave\n");

	return B_OK;
}


status_t
PluginManager::CreateDecoder(Decoder** decoder, const media_codec_info& mci)
{
	TRACE("PluginManager::CreateDecoder enter\n");
	entry_ref ref;
	status_t status = AddOnManager::GetInstance()->GetEncoder(&ref, mci.id);
	if (status != B_OK)
		return status;

	MediaPlugin* plugin = GetPlugin(ref);
	if (plugin == NULL) {
		ERROR("PluginManager::CreateDecoder: GetPlugin failed\n");
		return B_ERROR;
	}

	DecoderPlugin* decoderPlugin = dynamic_cast<DecoderPlugin*>(plugin);
	if (decoderPlugin == NULL) {
		ERROR("PluginManager::CreateDecoder: dynamic_cast failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}

	// TODO: In theory, one DecoderPlugin could support multiple Decoders,
	// but this is not yet handled (passing "0" as index/ID).
	*decoder = decoderPlugin->NewDecoder(0);
	if (*decoder == NULL) {
		ERROR("PluginManager::CreateDecoder: NewDecoder() failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}
	TRACE("  created decoder: %p\n", *decoder);
	(*decoder)->fMediaPlugin = plugin;

	TRACE("PluginManager::CreateDecoder leave\n");

	return B_OK;

}


status_t
PluginManager::GetDecoderInfo(Decoder* decoder, media_codec_info* _info) const
{
	if (decoder == NULL)
		return B_BAD_VALUE;

	decoder->GetCodecInfo(_info);
	// TODO:
	// out_info->id = 
	// out_info->sub_id = 
	return B_OK;
}


void
PluginManager::DestroyDecoder(Decoder* decoder)
{
	if (decoder != NULL) {
		TRACE("PluginManager::DestroyDecoder(%p, plugin: %p)\n", decoder,
			decoder->fMediaPlugin);
		// NOTE: We have to put the plug-in after deleting the decoder,
		// since otherwise we may actually unload the code for the
		// destructor...
		MediaPlugin* plugin = decoder->fMediaPlugin;
		delete decoder;
		PutPlugin(plugin);
	}
}


// #pragma mark - Writers/Encoders


status_t
PluginManager::CreateWriter(Writer** writer, const media_file_format& mff,
	BDataIO* target)
{
	TRACE("PluginManager::CreateWriter enter\n");

	// Get the Writer responsible for this media_file_format from the server.
	entry_ref ref;
	status_t ret = AddOnManager::GetInstance()->GetWriter(&ref,
		mff.id.internal_id);
	if (ret != B_OK) {
		printf("PluginManager::CreateWriter: can't get writer for file "
			"family: %s\n", strerror(ret));
		return ret;
	}

	MediaPlugin* plugin = GetPlugin(ref);
	if (plugin == NULL) {
		printf("PluginManager::CreateWriter: GetPlugin failed\n");
		return B_ERROR;
	}

	WriterPlugin* writerPlugin = dynamic_cast<WriterPlugin*>(plugin);
	if (writerPlugin == NULL) {
		printf("PluginManager::CreateWriter: dynamic_cast failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}

	*writer = writerPlugin->NewWriter();
	if (*writer == NULL) {
		printf("PluginManager::CreateWriter: NewWriter failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}

	(*writer)->Setup(target);
	(*writer)->fMediaPlugin = plugin;

	TRACE("PluginManager::CreateWriter leave\n");
	return B_OK;
}


void
PluginManager::DestroyWriter(Writer* writer)
{
	if (writer != NULL) {
		TRACE("PluginManager::DestroyWriter(%p (plugin: %p))\n", writer,
			writer->fMediaPlugin);
		// NOTE: We have to put the plug-in after deleting the writer,
		// since otherwise we may actually unload the code for the
		// destructor...
		MediaPlugin* plugin = writer->fMediaPlugin;
		delete writer;
		PutPlugin(plugin);
	}
}


status_t
PluginManager::CreateEncoder(Encoder** _encoder,
	const media_codec_info* codecInfo, uint32 flags)
{
	TRACE("PluginManager::CreateEncoder enter\n");

	// Get encoder for this codec info from the server
	entry_ref ref;
	status_t ret = AddOnManager::GetInstance()->GetEncoder(&ref,
		codecInfo->id);
	if (ret != B_OK) {
		printf("PluginManager::CreateEncoder: can't get encoder for codec %s: "
			"%s\n", codecInfo->pretty_name, strerror(ret));
		return ret;
	}

	MediaPlugin* plugin = GetPlugin(ref);
	if (!plugin) {
		printf("PluginManager::CreateEncoder: GetPlugin failed\n");
		return B_ERROR;
	}
	
	EncoderPlugin* encoderPlugin = dynamic_cast<EncoderPlugin*>(plugin);
	if (encoderPlugin == NULL) {
		printf("PluginManager::CreateEncoder: dynamic_cast failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}

	*_encoder = encoderPlugin->NewEncoder(*codecInfo);
	if (*_encoder == NULL) {
		printf("PluginManager::CreateEncoder: NewEncoder() failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}
	TRACE("  created encoder: %p\n", *_encoder);
	(*_encoder)->fMediaPlugin = plugin;

	TRACE("PluginManager::CreateEncoder leave\n");

	return B_OK;
}


status_t
PluginManager::CreateEncoder(Encoder** encoder, const media_format& format)
{
	TRACE("PluginManager::CreateEncoder enter nr2\n");

	entry_ref ref;

	status_t ret = AddOnManager::GetInstance()->GetEncoderForFormat(
		&ref, format);

	if (ret != B_OK) {
		ERROR("PluginManager::CreateEncoder: can't get decoder for format: "
			"%s\n", strerror(ret));
		return ret;
	}

	MediaPlugin* plugin = GetPlugin(ref);
	if (plugin == NULL) {
		ERROR("PluginManager::CreateEncoder: GetPlugin failed\n");
		return B_ERROR;
	}

	EncoderPlugin* encoderPlugin = dynamic_cast<EncoderPlugin*>(plugin);
	if (encoderPlugin == NULL) {
		ERROR("PluginManager::CreateEncoder: dynamic_cast failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}


	*encoder = encoderPlugin->NewEncoder(format);
	if (*encoder == NULL) {
		ERROR("PluginManager::CreateEncoder: NewEncoder() failed\n");
		PutPlugin(plugin);
		return B_ERROR;
	}
	TRACE("  created encoder: %p\n", *encoder);
	(*encoder)->fMediaPlugin = plugin;

	TRACE("PluginManager::CreateEncoder leave nr2\n");

	return B_OK;
}


void
PluginManager::DestroyEncoder(Encoder* encoder)
{
	if (encoder != NULL) {
		TRACE("PluginManager::DestroyEncoder(%p, plugin: %p)\n", encoder,
			encoder->fMediaPlugin);
		// NOTE: We have to put the plug-in after deleting the encoder,
		// since otherwise we may actually unload the code for the
		// destructor...
		MediaPlugin* plugin = encoder->fMediaPlugin;
		delete encoder;
		PutPlugin(plugin);
	}
}


status_t
PluginManager::CreateStreamer(Streamer** streamer, BUrl url, BDataIO** source)
{
	BAutolock _(fLocker);

	TRACE("PluginManager::CreateStreamer enter\n");

	entry_ref refs[MAX_STREAMERS];
	int32 count;

	status_t ret = AddOnManager::GetInstance()->GetStreamers(refs, &count,
		MAX_STREAMERS);
	if (ret != B_OK) {
		printf("PluginManager::CreateStreamer: can't get list of streamers:"
			" %s\n", strerror(ret));
		return ret;
	}

	// try each reader by calling it's Sniff function...
	for (int32 i = 0; i < count; i++) {
		entry_ref ref = refs[i];
		MediaPlugin* plugin = GetPlugin(ref);
		if (plugin == NULL) {
			printf("PluginManager::CreateStreamer: GetPlugin failed\n");
			return B_ERROR;
		}

		StreamerPlugin* streamerPlugin = dynamic_cast<StreamerPlugin*>(plugin);
		if (streamerPlugin == NULL) {
			printf("PluginManager::CreateStreamer: dynamic_cast failed\n");
			PutPlugin(plugin);
			return B_ERROR;
		}

		*streamer = streamerPlugin->NewStreamer();
		if (*streamer == NULL) {
			printf("PluginManager::CreateStreamer: NewReader failed\n");
			PutPlugin(plugin);
			return B_ERROR;
		}

		(*streamer)->fMediaPlugin = plugin;
		plugin->fRefCount++;

		BDataIO* streamSource = NULL;
		if ((*streamer)->Sniff(url, &streamSource) == B_OK) {
			TRACE("PluginManager::CreateStreamer: Sniff success\n");
			*source = streamSource;
			return B_OK;
		}

		DestroyStreamer(*streamer);
		*streamer = NULL;
	}

	TRACE("PluginManager::CreateStreamer leave\n");
	return B_MEDIA_NO_HANDLER;
}


void
PluginManager::DestroyStreamer(Streamer* streamer)
{
	BAutolock _(fLocker);

	if (streamer != NULL) {
		TRACE("PluginManager::DestroyStreamer(%p, plugin: %p)\n", streamer,
			streamer->fMediaPlugin);

		// NOTE: We have to put the plug-in after deleting the streamer,
		// since otherwise we may actually unload the code for the
		// destructor...
		MediaPlugin* plugin = streamer->fMediaPlugin;
		delete streamer;

		// Delete the plugin only when every reference is released
		if (plugin->fRefCount == 1) {
			plugin->fRefCount = 0;
			PutPlugin(plugin);
		} else
			plugin->fRefCount--;
	}
}


// #pragma mark -


PluginManager::PluginManager()
	:
	fPluginList(),
	fLocker("media plugin manager")
{
	CALLED();
}


PluginManager::~PluginManager()
{
	CALLED();
	for (int i = fPluginList.CountItems() - 1; i >= 0; i--) {
		plugin_info* info = NULL;
		fPluginList.Get(i, &info);
		TRACE("PluginManager: Error, unloading PlugIn %s with usecount "
			"%d\n", info->name, info->usecount);
		delete info->plugin;
		unload_add_on(info->image);
	}
}

	
MediaPlugin*
PluginManager::GetPlugin(const entry_ref& ref)
{
	TRACE("PluginManager::GetPlugin(%s)\n", ref.name);
	fLocker.Lock();
	
	MediaPlugin* plugin;
	plugin_info* pinfo;
	plugin_info info;
	
	for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) {
		if (0 == strcmp(ref.name, pinfo->name)) {
			plugin = pinfo->plugin;
			pinfo->usecount++;
			TRACE("  found existing plugin: %p\n", pinfo->plugin);
			fLocker.Unlock();
			return plugin;
		}
	}

	if (_LoadPlugin(ref, &info.plugin, &info.image) < B_OK) {
		printf("PluginManager: Error, loading PlugIn %s failed\n", ref.name);
		fLocker.Unlock();
		return NULL;
	}

	strcpy(info.name, ref.name);
	info.usecount = 1;
	fPluginList.Insert(info);
	
	TRACE("PluginManager: PlugIn %s loaded\n", ref.name);

	plugin = info.plugin;
	TRACE("  loaded plugin: %p\n", plugin);
	
	fLocker.Unlock();
	return plugin;
}


void
PluginManager::PutPlugin(MediaPlugin* plugin)
{
	TRACE("PluginManager::PutPlugin()\n");
	fLocker.Lock();
	
	plugin_info* pinfo;
	
	for (fPluginList.Rewind(); fPluginList.GetNext(&pinfo); ) {
		if (plugin == pinfo->plugin) {
			pinfo->usecount--;
			if (pinfo->usecount == 0) {
				TRACE("  deleting %p\n", pinfo->plugin);
				delete pinfo->plugin;
				TRACE("  unloading add-on: %" B_PRId32 "\n\n", pinfo->image);
				unload_add_on(pinfo->image);
				fPluginList.RemoveCurrent();
			}
			fLocker.Unlock();
			return;
		}
	}
	
	printf("PluginManager: Error, can't put PlugIn %p\n", plugin);
	
	fLocker.Unlock();
}


status_t
PluginManager::_LoadPlugin(const entry_ref& ref, MediaPlugin** plugin,
	image_id* image)
{
	BPath p(&ref);

	TRACE("PluginManager: _LoadPlugin trying to load %s\n", p.Path());

	image_id id;
	id = load_add_on(p.Path());
	if (id < 0) {
		printf("PluginManager: Error, load_add_on(): %s\n", strerror(id));
		return B_ERROR;
	}
		
	MediaPlugin* (*instantiate_plugin_func)();
	
	if (get_image_symbol(id, "instantiate_plugin", B_SYMBOL_TYPE_TEXT,
			(void**)&instantiate_plugin_func) < B_OK) {
		printf("PluginManager: Error, _LoadPlugin can't find "
			"instantiate_plugin in %s\n", p.Path());
		unload_add_on(id);
		return B_ERROR;
	}
	
	MediaPlugin *pl;
	
	pl = (*instantiate_plugin_func)();
	if (pl == NULL) {
		printf("PluginManager: Error, _LoadPlugin instantiate_plugin in %s "
			"returned NULL\n", p.Path());
		unload_add_on(id);
		return B_ERROR;
	}
	
	*plugin = pl;
	*image = id;
	return B_OK;
}