示例#1
0
文件: playlist.c 项目: jonsafari/mocp
/* Set the time tags field for the item. */
void plist_set_item_time (struct plist *plist, const int num, const int time)
{
	int old_time;

	assert (plist != NULL);
	assert (LIMIT(num, plist->num));

	if (!plist->items[num].tags) {
		plist->items[num].tags = tags_new ();
		old_time = -1;
	}
	else if (plist->items[num].tags->time != -1)
		old_time = plist->items[num].tags->time;
	else
		old_time = -1;

	if (old_time != -1) {
		plist->total_time -= old_time;
		plist->items_with_time--;
	}

	if (time != -1) {
		plist->total_time += time;
		plist->items_with_time++;
	}

	plist->items[num].tags->time = time;
	plist->items[num].tags->filled |= TAGS_TIME;
}
示例#2
0
文件: playlist.c 项目: jonsafari/mocp
struct file_tags *tags_dup (const struct file_tags *tags)
{
	struct file_tags *dtags;

	assert (tags != NULL);

	dtags = tags_new();
	tags_copy (dtags, tags);

	return dtags;
}
示例#3
0
/* Read selected tags for a file into tags structure (or create it if NULL).
 * If some tags are already present, don't read them.
 * If present_tags is NULL, allocate new tags. */
struct file_tags *read_file_tags (const char *file,
		struct file_tags *present_tags, const int tags_sel)
{
	struct file_tags *tags;
	struct decoder *df;
	int needed_tags;

	assert (file != NULL);

	if (present_tags) {
		tags = present_tags;
		needed_tags = ~tags->filled & tags_sel;
	}
	else {
		tags = tags_new ();
		needed_tags = tags_sel;
	}

	if (file_type(file) == F_URL)
		return tags;

	df = get_decoder (file);

	if (!df) {
		logit ("Can't find decoder functions for %s", file);
		return tags;
	}

	if (needed_tags) {

		/* This makes sure that we don't cause a memory leak */
		assert (!((needed_tags & TAGS_COMMENTS) &&
					(tags->title
					 || tags->artist
					 || tags->album)));

		df->info (file, tags, needed_tags);
		tags->filled |= tags_sel;
	}
	else
		debug ("No need to read any tags");
	
	return tags;
}
示例#4
0
文件: player.c 项目: Manishearth/moc
/* Update tags if tags from the decoder or the stream are available. */
static void update_tags (const struct decoder *f, void *decoder_data,
		struct io_stream *s)
{
	char *stream_title = NULL;
	int tags_changed = 0;
	struct file_tags *new_tags;

	new_tags = tags_new ();

	LOCK (curr_tags_mut);
	if (f->current_tags && f->current_tags(decoder_data, new_tags)
			&& new_tags->title) {
		tags_changed = 1;
		tags_copy (curr_tags, new_tags);
		logit ("Tags change from the decoder");
		tags_source = TAGS_SOURCE_DECODER;
		show_tags (curr_tags);
	}
	else if (s && (stream_title = io_get_metadata_title(s))) {
		if (curr_tags && curr_tags->title
				&& tags_source == TAGS_SOURCE_DECODER) {
			logit ("New IO stream tags, ignored because there are "
					"decoder tags present");
			free (stream_title);
		}
		else {
			tags_clear (curr_tags);
			curr_tags->title = stream_title;
			show_tags (curr_tags);
			tags_changed = 1;
			logit ("New IO stream tags");
			tags_source = TAGS_SOURCE_METADATA;
		}
	}

	if (tags_changed)
		tags_change ();

	tags_free (new_tags);

	UNLOCK (curr_tags_mut);
}
示例#5
0
/* Read time tags for a file into tags structure (or create it if NULL). */
struct file_tags *read_missing_tags (const char *file,
                 struct file_tags *tags, int tags_sel)
{
	if (tags == NULL)
		tags = tags_new ();

	if (tags_sel & TAGS_TIME) {
		int time;

		/* Try to get it from the server's playlist first. */
		time = audio_get_ftime (file);

		if (time != -1) {
			tags->time = time;
			tags->filled |= TAGS_TIME;
			tags_sel &= ~TAGS_TIME;
		}
	}

	tags = read_file_tags (file, tags, tags_sel);

	return tags;
}
示例#6
0
/* Read the selected tags for this file and add it to the cache.
 * If client_id != -1, the server is notified using tags_response().
 * If client_id == -1, copy of file_tags is returned. */
static struct file_tags *tags_cache_read_add (struct tags_cache *c,
		const int client_id, const char *file, int tags_sel)
{
	struct file_tags *tags = NULL;
	DBT key;
	DBT serialized_cache_rec;
	DB_LOCK lock;
	int got_lock = 0;
	int ret;

	assert (c != NULL);
	assert (c->db != NULL);
	assert (file != NULL);

	debug ("Getting tags for %s", file);

	memset (&key, 0, sizeof(key));
	memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec));

	key.data = (void *)file;
	key.size = strlen(file);
	serialized_cache_rec.flags = DB_DBT_MALLOC;

	ret = c->db_env->lock_get (c->db_env, c->locker, 0,
			&key, DB_LOCK_WRITE, &lock);
	if (ret) {
		logit ("Can't get DB lock: %s", db_strerror(ret));
	}
	else {
		got_lock = 1;
		ret = c->db->get (c->db, NULL, &key, &serialized_cache_rec, 0);
		if (ret && ret != DB_NOTFOUND)
			logit ("Cache DB get error: %s", db_strerror(ret));
	}

	/* If this entry is already present in the cache, we have 3 options:
	 * we must read different tags (TAGS_*) or the tags are outdated
	 * or this is an immediate tags read (client_id == -1) */
	if (ret == 0) {
		struct cache_record rec;

		if (cache_record_deserialize (&rec, serialized_cache_rec.data,
		                              serialized_cache_rec.size, 0)) {
			time_t curr_mtime = get_mtime (file);

			if (rec.mod_time != curr_mtime) {

				/* outdated tags - remove them and reread */
				tags_free (rec.tags);

				debug ("Tags in the cache are outdated");
			}
			else if ((rec.tags->filled & tags_sel) == tags_sel
					&& client_id == -1) {
				debug ("Tags are in the cache.");
				tags = rec.tags;

				goto end;
			}
			else {
				tags = rec.tags; /* read tags in addition to already
						      present tags */
				debug ("Tags in the cache are not what we want.");
			}
		}
	}

	if (tags == NULL)
		tags = tags_new ();

	if (tags_sel & TAGS_TIME) {
		int time;

		/* Try to get it from the server's playlist first. */
		time = audio_get_ftime (file);

		if (time != -1) {
			tags->time = time;
			tags->filled |= TAGS_TIME;
			tags_sel &= ~TAGS_TIME;
		}
	}

	tags = read_file_tags (file, tags, tags_sel);

	debug ("Adding/updating cache object");
	tags_cache_add (c, file, tags);

	if (client_id != -1) {
		tags_response (client_id, file, tags);
		tags_free (tags);
		tags = NULL;
	}

	/* TODO: Remove the oldest items from the cache if we exceeded the maximum
	 * cache size */

end:
	if (got_lock) {
		ret = c->db_env->lock_put (c->db_env, &lock);
		if (ret)
			logit ("Can't release DB lock: %s", db_strerror(ret));
	}

	if (serialized_cache_rec.data)
		free (serialized_cache_rec.data);

	return tags;
}
示例#7
0
static int cache_record_deserialize (struct cache_record *rec,
		const char *serialized, const size_t size,
		const int skip_tags)
{
	const char *p = serialized;
	size_t bytes_left = size;
	size_t str_len;

	assert (rec != NULL);
	assert (serialized != NULL);

	if (!skip_tags)
		rec->tags = tags_new ();
	else
		rec->tags = NULL;

#define extract_num(var)			\
	if (bytes_left < sizeof(var))		\
		goto err;			\
	memcpy (&var, p, sizeof(var));		\
	bytes_left -= sizeof(var);		\
	p += sizeof(var);

#define extract_str(var)			\
	if (bytes_left < sizeof(str_len))	\
		goto err;			\
	memcpy (&str_len, p, sizeof(str_len));	\
	p += sizeof(str_len);			\
	if (bytes_left < str_len)		\
		goto err;			\
	var = xmalloc (str_len + 1);		\
	memcpy (var, p, str_len);		\
	var[str_len] = '\0';			\
	p += str_len;

	extract_num (rec->mod_time);
	extract_num (rec->atime);

	if (!skip_tags) {
		extract_str (rec->tags->artist);
		extract_str (rec->tags->album);
		extract_str (rec->tags->title);
		extract_num (rec->tags->track);
		extract_num (rec->tags->time);

		if (rec->tags->title)
			rec->tags->filled |= TAGS_COMMENTS;
		else {
			if (rec->tags->artist)
				free (rec->tags->artist);
			rec->tags->artist = NULL;

			if (rec->tags->album)
				free (rec->tags->album);
			rec->tags->album = NULL;

		}

		if (rec->tags->time >= 0)
			rec->tags->filled |= TAGS_TIME;
	}

	return 1;

err:
	logit ("Cache record deserialization error at %ldB", (long) (p - serialized));
	tags_free (rec->tags);
	rec->tags = NULL;
	return 0;
}
示例#8
0
文件: player.c 项目: Manishearth/moc
/* Decoder loop for already opened and probably running for some time decoder.
 * next_file will be precached at eof. */
static void decode_loop (const struct decoder *f, void *decoder_data,
		const char *next_file, struct out_buf *out_buf,
		struct sound_params *sound_params, struct md5_data *md5,
		const float already_decoded_sec)
{
	bool eof = false;
	bool stopped = false;
	char buf[PCM_BUF_SIZE];
	int decoded = 0;
	struct sound_params new_sound_params;
	bool sound_params_change = false;
	float decode_time = already_decoded_sec; /* the position of the decoder
						    (in seconds) */

	out_buf_set_free_callback (out_buf, buf_free_callback);

	LOCK (curr_tags_mut);
	curr_tags = tags_new ();
	UNLOCK (curr_tags_mut);

	if (f->get_stream) {
		LOCK (decoder_stream_mut);
		decoder_stream = f->get_stream (decoder_data);
		UNLOCK (decoder_stream_mut);
	}
	else
		logit ("No get_stream() function");

	status_msg ("Playing...");

	while (1) {
		debug ("loop...");

		LOCK (request_cond_mutex);
		if (!eof && !decoded) {
			struct decoder_error err;

			UNLOCK (request_cond_mutex);

			if (decoder_stream && out_buf_get_fill(out_buf)
					< PREBUFFER_THRESHOLD) {
				prebuffering = 1;
				io_prebuffer (decoder_stream,
						options_get_int("Prebuffering")
						* 1024);
				prebuffering = 0;
				status_msg ("Playing...");
			}

			decoded = f->decode (decoder_data, buf, sizeof(buf),
					&new_sound_params);

			if (decoded)
				decode_time += decoded / (float)(sfmt_Bps(
							new_sound_params.fmt) *
						new_sound_params.rate *
						new_sound_params.channels);

			f->get_error (decoder_data, &err);
			if (err.type != ERROR_OK) {
				md5->okay = false;
				if (err.type != ERROR_STREAM
						|| options_get_int(
							"ShowStreamErrors"))
					error ("%s", err.err);
				decoder_error_clear (&err);
			}

			if (!decoded) {
				eof = true;
				logit ("EOF from decoder");
			}
			else {
				debug ("decoded %d bytes", decoded);
				if (!sound_params_eq(new_sound_params, *sound_params))
					sound_params_change = true;

				bitrate_list_add (&bitrate_list, decode_time,
						f->get_bitrate(decoder_data));
				update_tags (f, decoder_data, decoder_stream);
			}
		}

		/* Wait, if there is no space in the buffer to put the decoded
		 * data or EOF occurred and there is something in the buffer. */
		else if (decoded > out_buf_get_free(out_buf)
					|| (eof && out_buf_get_fill(out_buf))) {
			debug ("waiting...");
			if (eof && !precache.file && next_file
					&& file_type(next_file) == F_SOUND
					&& options_get_int("Precache")
					&& options_get_bool("AutoNext"))
				start_precache (&precache, next_file);
			pthread_cond_wait (&request_cond, &request_cond_mutex);
			UNLOCK (request_cond_mutex);
		}
		else
			UNLOCK (request_cond_mutex);

		/* When clearing request, we must make sure, that another
		 * request will not arrive at the moment, so we check if
		 * the request has changed. */
		if (request == REQ_STOP) {
			logit ("stop");
			stopped = true;
			md5->okay = false;
			out_buf_stop (out_buf);

			LOCK (request_cond_mutex);
			if (request == REQ_STOP)
				request = REQ_NOTHING;
			UNLOCK (request_cond_mutex);

			break;
		}
		else if (request == REQ_SEEK) {
			int decoder_seek;

			logit ("seeking");
			md5->okay = false;
			req_seek = MAX(0, req_seek);
			if ((decoder_seek = f->seek(decoder_data, req_seek))
					== -1)
				logit ("error when seeking");
			else {
				out_buf_stop (out_buf);
				out_buf_reset (out_buf);
				out_buf_time_set (out_buf, decoder_seek);
				bitrate_list_empty (&bitrate_list);
				decode_time = decoder_seek;
				eof = false;
				decoded = 0;
			}

			LOCK (request_cond_mutex);
			if (request == REQ_SEEK)
				request = REQ_NOTHING;
			UNLOCK (request_cond_mutex);

		}
		else if (!eof && decoded <= out_buf_get_free(out_buf)
				&& !sound_params_change) {
			debug ("putting into the buffer %d bytes", decoded);
#if !defined(NDEBUG) && defined(DEBUG)
			if (md5->okay) {
				md5->len += decoded;
				md5_process_bytes (buf, decoded, &md5->ctx);
			}
#endif
			audio_send_buf (buf, decoded);
			decoded = 0;
		}
		else if (!eof && sound_params_change
				&& out_buf_get_fill(out_buf) == 0) {
			logit ("Sound parameters have changed.");
			*sound_params = new_sound_params;
			sound_params_change = false;
			set_info_channels (sound_params->channels);
			set_info_rate (sound_params->rate / 1000);
			out_buf_wait (out_buf);
			if (!audio_open(sound_params)) {
				md5->okay = false;
				break;
			}
		}
		else if (eof && out_buf_get_fill(out_buf) == 0) {
			logit ("played everything");
			break;
		}
	}

	status_msg ("");

	LOCK (decoder_stream_mut);
	decoder_stream = NULL;
	f->close (decoder_data);
	UNLOCK (decoder_stream_mut);

	bitrate_list_destroy (&bitrate_list);

	LOCK (curr_tags_mut);
	if (curr_tags) {
		tags_free (curr_tags);
		curr_tags = NULL;
	}
	UNLOCK (curr_tags_mut);

	out_buf_wait (out_buf);

	if (precache.ok && (stopped || !options_get_bool ("AutoNext"))) {
		precache_wait (&precache);
		precache.f->close (precache.decoder_data);
		precache_reset (&precache);
	}
}
示例#9
0
/* Load PLS file into plist. Return the number of items read. */
static int plist_load_pls (struct plist *plist, const char *fname,
		const char *cwd)
{
	FILE *file;
	char *line;
	long i, nitems, added = 0;
	char *e;

	if (!(file = fopen(fname, "r"))) {
		error ("Can't open playlist file: %s",
				strerror(errno));
		return 0;
	}

	line = read_ini_value (file, "playlist", "NumberOfEntries");
	if (!line) {

		/* Assume that it is a pls file version 1 - plist_load_m3u()
		 * should handle it like an m3u file without the m3u extensions. */
		fclose (file);
		return plist_load_m3u (plist, fname, cwd, 0);
	}

	nitems = strtol (line, &e, 10);
	if (*e) {
		error ("Broken PLS file");
		free (line);
		return 0;
	}
	free (line);

	for (i = 1; i <= nitems; i++) {
		char *pls_file, *pls_title, *pls_length;
		char key[16];
		int time;
		int last_added;
		char path[2*PATH_MAX];

		sprintf (key, "File%ld", i);
		if (!(pls_file = read_ini_value(file, "playlist", key))) {
			error ("Broken PLS file");
			break;
		}

		sprintf (key, "Title%ld", i);
		pls_title = read_ini_value(file, "playlist", key);

		sprintf (key, "Length%ld", i);
		pls_length = read_ini_value(file, "playlist", key);

		if (pls_length) {
			time = strtol (pls_length, &e, 10);
			if (*e)
				time = -1;
		}
		else
			time = -1;

		if (strlen(pls_file) <= PATH_MAX) {
			make_path (path, sizeof(path), cwd, pls_file);
			if (plist_find_fname(plist, path) == -1) {
				last_added = plist_add (plist, path);

				if (pls_title && pls_title[0])
					plist_set_title_tags (plist, last_added,
							pls_title);

				if (time > 0) {
					plist->items[last_added].tags
						= tags_new ();
					plist->items[last_added].tags->time
						= time;
					plist->items[last_added].tags->filled
						|= TAGS_TIME;
				}
			}
		}

		free (pls_file);
		if (pls_title)
			free (pls_title);
		if (pls_length)
			free (pls_length);
		added++;
	}

	fclose (file);

	return added;
}