static struct input_stream *
bz2_open_stream(struct archive_file *file, const char *path,
		GMutex *mutex, GCond *cond,
		GError **error_r)
{
	struct bz2_archive_file *context = (struct bz2_archive_file *) file;
	struct bz2_input_stream *bis = g_new(struct bz2_input_stream, 1);

	input_stream_init(&bis->base, &bz2_inputplugin, path,
			  mutex, cond);

	bis->archive = context;

	bis->base.ready = true;
	bis->base.seekable = false;

	if (!bz2_alloc(bis, error_r)) {
		input_stream_deinit(&bis->base);
		g_free(bis);
		return NULL;
	}

	bis->eof = false;

	refcount_inc(&context->ref);

	return &bis->base;
}
static struct input_stream *
input_despotify_open(const char *url, G_GNUC_UNUSED GError **error_r)
{
	struct input_despotify *ctx;
	struct despotify_session *session;
	struct ds_link *ds_link;
	struct ds_track *track;

	if (!g_str_has_prefix(url, "spt://"))
		return NULL;

	session = mpd_despotify_get_session();
	if (!session)
		return NULL;

	ds_link = despotify_link_from_uri(url + 6);
	if (!ds_link) {
		g_debug("Can't find %s\n", url);
		return NULL;
	}
	if (ds_link->type != LINK_TYPE_TRACK) {
		despotify_free_link(ds_link);
		return NULL;
	}

	ctx = g_new(struct input_despotify, 1);
	memset(ctx, 0, sizeof(*ctx));

	track = despotify_link_get_track(session, ds_link);
	despotify_free_link(ds_link);
	if (!track) {
		g_free(ctx);
		return NULL;
	}

	input_stream_init(&ctx->base, &input_plugin_despotify, url);
	ctx->session = session;
	ctx->track = track;
	ctx->tag = mpd_despotify_tag_from_track(track);
	ctx->eof = false;
	/* Despotify outputs pcm data */
	ctx->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
	ctx->base.ready = true;

	if (!mpd_despotify_register_callback(callback, ctx)) {
		despotify_free_link(ds_link);

		return NULL;
	}

	if (despotify_play(ctx->session, ctx->track, false) == false) {
		despotify_free_track(ctx->track);
		g_free(ctx);
		return NULL;
	}

	return &ctx->base;
}
static struct input_stream *
input_ffmpeg_open(const char *uri,
		  GMutex *mutex, GCond *cond,
		  GError **error_r)
{
	struct input_ffmpeg *i;

	if (!g_str_has_prefix(uri, "gopher://") &&
	    !g_str_has_prefix(uri, "rtp://") &&
	    !g_str_has_prefix(uri, "rtsp://") &&
	    !g_str_has_prefix(uri, "rtmp://") &&
	    !g_str_has_prefix(uri, "rtmpt://") &&
	    !g_str_has_prefix(uri, "rtmps://"))
		return NULL;

	i = g_new(struct input_ffmpeg, 1);
	input_stream_init(&i->base, &input_plugin_ffmpeg, uri,
			  mutex, cond);

#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
	int ret = avio_open(&i->h, uri, AVIO_FLAG_READ);
#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
	int ret = avio_open(&i->h, uri, AVIO_RDONLY);
#else
	int ret = url_open(&i->h, uri, URL_RDONLY);
#endif
	if (ret != 0) {
		g_free(i);
		g_set_error(error_r, ffmpeg_quark(), ret,
			    "libavformat failed to open the URI");
		return NULL;
	}

	i->eof = false;

	i->base.ready = true;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
	i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
	i->base.size = avio_size(i->h);
#else
	i->base.seekable = !i->h->is_streamed;
	i->base.size = url_filesize(i->h);
#endif

	/* hack to make MPD select the "ffmpeg" decoder plugin - since
	   avio.h doesn't tell us the MIME type of the resource, we
	   can't select a decoder plugin, but the "ffmpeg" plugin is
	   quite good at auto-detection */
	i->base.mime = g_strdup("audio/x-mpd-ffmpeg");

	return &i->base;
}
static struct input_stream *
input_file_open(const char *filename, GError **error_r)
{
	int fd, ret;
	struct stat st;
	struct file_input_stream *fis;

	if (!g_path_is_absolute(filename))
		return false;

	fd = open_cloexec(filename, O_RDONLY, 0);
	if (fd < 0) {
		if (errno != ENOENT && errno != ENOTDIR)
			g_set_error(error_r, file_quark(), errno,
				    "Failed to open \"%s\": %s",
				    filename, g_strerror(errno));
		return false;
	}

	ret = fstat(fd, &st);
	if (ret < 0) {
		g_set_error(error_r, file_quark(), errno,
			    "Failed to stat \"%s\": %s",
			    filename, g_strerror(errno));
		close(fd);
		return false;
	}

	if (!S_ISREG(st.st_mode)) {
		g_set_error(error_r, file_quark(), 0,
			    "Not a regular file: %s", filename);
		close(fd);
		return false;
	}

#ifdef POSIX_FADV_SEQUENTIAL
	posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif

	fis = g_new(struct file_input_stream, 1);
	input_stream_init(&fis->base, &input_plugin_file, filename);

	fis->base.size = st.st_size;
	fis->base.seekable = true;
	fis->base.ready = true;

	fis->fd = fd;

	return &fis->base;
}
static struct input_stream *
input_curl_open(const char *url, GError **error_r)
{
	struct input_curl *c;
	bool ret;

	if (strncmp(url, "http://", 7) != 0)
		return NULL;

	c = g_new0(struct input_curl, 1);
	input_stream_init(&c->base, &input_plugin_curl, url);

	c->url = g_strdup(url);
	c->buffers = g_queue_new();

	c->multi = curl_multi_init();
	if (c->multi == NULL) {
		g_set_error(error_r, curl_quark(), 0,
			    "curl_multi_init() failed");
		input_curl_free(c);
		return NULL;
	}

	icy_clear(&c->icy_metadata);
	c->tag = NULL;

	ret = input_curl_easy_init(c, error_r);
	if (!ret) {
		input_curl_free(c);
		return NULL;
	}

	ret = input_curl_send_request(c, error_r);
	if (!ret) {
		input_curl_free(c);
		return NULL;
	}

	ret = input_curl_multi_info_read(c, error_r);
	if (!ret) {
		input_curl_free(c);
		return NULL;
	}

	return &c->base;
}
static struct input_stream *
input_file_open(const char *filename,
		GMutex *mutex, GCond *cond,
		GError **error_r)
{
	int fd, ret;
	struct stat st;
	struct file_input_stream *fis;

	if (!g_path_is_absolute(filename))
		return NULL;

	fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0);
        if (fd < 0)  {
		// the filename doesn't exist	
		// try and open uri as if it were a track inside a container
		char* pathname = NULL;
		unsigned tnum;

		pathname = g_strdup(filename);
		remove_suffix(pathname,'/');

		tnum = cue_vtrack_tnum(filename);
		if ( tnum == 0 )
			tnum=1; // use filename from first track of cue file if no track found 
			
                if( g_str_has_suffix(pathname,".cue"))
                    pathname=cue_locate_audio_container_file(pathname,tnum) ;

		fd = open_cloexec(pathname, O_RDONLY|O_BINARY, 0);
		g_free(pathname);

		if (fd < 0) {
			if (errno != ENOENT && errno != ENOTDIR)
				g_set_error(error_r, file_quark(), errno,
						"Failed to open \"%s\": %s",
						filename, g_strerror(errno));
			return NULL;
		}
	}

	ret = fstat(fd, &st);
	if (ret < 0) {
		g_set_error(error_r, file_quark(), errno,
			    "Failed to stat \"%s\": %s",
			    filename, g_strerror(errno));
		close(fd);
		return NULL;
	}

	if (!S_ISREG(st.st_mode)) {
		g_set_error(error_r, file_quark(), 0,
			    "Not a regular file: %s", filename);
		close(fd);
		return NULL;
	}

#ifdef POSIX_FADV_SEQUENTIAL
	posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif

	fis = g_new(struct file_input_stream, 1);
	input_stream_init(&fis->base, &input_plugin_file, filename,
			  mutex, cond);

	fis->base.size = st.st_size;
	fis->base.seekable = true;
	fis->base.ready = true;

	fis->fd = fd;

	return &fis->base;
}
static struct input_stream *
input_cdio_open(const char *uri,
		GMutex *mutex, GCond *cond,
		GError **error_r)
{
	struct input_cdio_paranoia *i;

	struct cdio_uri parsed_uri;
	if (!parse_cdio_uri(&parsed_uri, uri, error_r))
		return NULL;

	i = g_new(struct input_cdio_paranoia, 1);
	input_stream_init(&i->base, &input_plugin_cdio_paranoia, uri,
			  mutex, cond);

	/* initialize everything (should be already) */
	i->drv = NULL;
	i->cdio = NULL;
	i->para = NULL;
	i->trackno = parsed_uri.track;
	pcm_buffer_init(&i->conv_buffer);

	/* get list of CD's supporting CD-DA */
	char *device = parsed_uri.device[0] != 0
		? g_strdup(parsed_uri.device)
		: cdio_detect_device();
	if (device == NULL) {
		g_set_error(error_r, cdio_quark(), 0,
			    "Unable find or access a CD-ROM drive with an audio CD in it.");
		input_cdio_close(&i->base);
		return NULL;
	}

	/* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */
	i->cdio = cdio_open(device, DRIVER_UNKNOWN);
	g_free(device);

	i->drv = cdio_cddap_identify_cdio(i->cdio, 1, NULL);

	if ( !i->drv ) {
		g_set_error(error_r, cdio_quark(), 0,
			    "Unable to identify audio CD disc.");
		input_cdio_close(&i->base);
		return NULL;
	}

	cdda_verbose_set(i->drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);

	if ( 0 != cdio_cddap_open(i->drv) ) {
		g_set_error(error_r, cdio_quark(), 0, "Unable to open disc.");
		input_cdio_close(&i->base);
		return NULL;
	}

	i->endian = data_bigendianp(i->drv);
	switch (i->endian) {
	case -1:
		g_debug("cdda: drive returns unknown audio data, assuming Little Endian");
		i->endian = 0;
		break;
	case 0:
		g_debug("cdda: drive returns audio data Little Endian.");
		break;
	case 1:
		g_debug("cdda: drive returns audio data Big Endian.");
		break;
	default:
		g_set_error(error_r, cdio_quark(), 0,
			    "Drive returns unknown data type %d", i->endian);
		input_cdio_close(&i->base);
		return NULL;
	}

	i->lsn_relofs = 0;

	if (i->trackno >= 0) {
		i->lsn_from = cdio_get_track_lsn(i->cdio, i->trackno);
		i->lsn_to = cdio_get_track_last_lsn(i->cdio, i->trackno);
	} else {
		i->lsn_from = 0;
		i->lsn_to = cdio_get_disc_last_lsn(i->cdio);
	}

	i->para = cdio_paranoia_init(i->drv);

	/* Set reading mode for full paranoia, but allow skipping sectors. */
	paranoia_modeset(i->para, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);

	/* seek to beginning of the track */
	cdio_paranoia_seek(i->para, i->lsn_from, SEEK_SET);

	i->base.ready = true;
	i->base.seekable = true;
	i->base.size = (i->lsn_to - i->lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;

	/* hack to make MPD select the "pcm" decoder plugin */
	i->base.mime = g_strdup("audio/x-mpd-cdda-pcm");

	return &i->base;
}