Example #1
0
int alsa_hook_start(alsa_hook_t alsa_hook)
{
	if (unlikely(!alsa_hook->to)) {
		glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook",
			 "target buffer not specified");
		return EAGAIN;
	}

	if (!alsa_hook->started)
		alsa_hook_init_streams(alsa_hook);

	if (alsa_hook->flags & ALSA_HOOK_CAPTURING)
		glc_log(alsa_hook->glc, GLC_WARN, "alsa_hook",
			 "capturing is already active");
	else
		glc_log(alsa_hook->glc, GLC_INFO, "alsa_hook",
			 "starting capturing");

	alsa_hook->flags |= ALSA_HOOK_CAPTURING;
	return 0;
}
Example #2
0
void print_stats(glc_t *glc, pack_stat_t *stat)
{
	double ratio;
	if (!stat->unpack_size)
		ratio = 0.0;
	else
		ratio = (double)stat->pack_size/(double)stat->unpack_size;

	glc_log(glc, GLC_PERF, "pack",
		"unpack_size: %llu pack_size: %llu %remn: %.1f",
		stat->unpack_size, stat->pack_size, ratio*100);
}
Example #3
0
int gl_capture_draw_indicator(gl_capture_t gl_capture, int draw_indicator)
{
	if (draw_indicator) {
		gl_capture->flags |= GL_CAPTURE_DRAW_INDICATOR;

		if (gl_capture->capture_buffer == GL_FRONT)
			glc_log(gl_capture->glc, GLC_WARN, "gl_capture",
				 "indicator doesn't work well when capturing from GL_FRONT");
	} else
		gl_capture->flags &= ~GL_CAPTURE_DRAW_INDICATOR;

	return 0;
}
Example #4
0
int audio_capture_write_cfg(audio_capture_t audio_capture)
{
	glc_message_header_t hdr;
	glc_audio_format_message_t fmt_msg;
	int ret = 0;

	if (!audio_capture->id)
		glc_state_audio_new(audio_capture->glc, &audio_capture->id,
				    &audio_capture->state_audio);

	hdr.type = GLC_MESSAGE_AUDIO_FORMAT;
	fmt_msg.id = audio_capture->id;
	fmt_msg.flags = audio_capture->format_flags;
	fmt_msg.rate = audio_capture->rate;
	fmt_msg.channels = audio_capture->channels;
	fmt_msg.format = audio_capture->format;

	if ((ret = ps_packet_open(&audio_capture->packet, PS_PACKET_WRITE)))
		goto err;
	if ((ret = ps_packet_write(&audio_capture->packet,
				   &hdr, sizeof(glc_message_header_t))))
		goto err;
	if ((ret = ps_packet_write(&audio_capture->packet,
				   &fmt_msg, sizeof(glc_audio_format_message_t))))
		goto err;
	if ((ret = ps_packet_close(&audio_capture->packet)))
		goto err;

	return 0;
err:
	glc_state_set(audio_capture->glc, GLC_STATE_CANCEL);
	ps_buffer_cancel(audio_capture->target);
	glc_log(audio_capture->glc, GLC_ERROR, "audio_capture",
		"can't write audio stream configuration to buffer");
	glc_log(audio_capture->glc, GLC_ERROR, "audio_capture",
		"%s (%d)", strerror(ret), ret);
	return ret;
}
Example #5
0
void gl_capture_error(gl_capture_t gl_capture, int err)
{
	glc_log(gl_capture->glc, GLC_ERROR, "gl_capture",
		"%s (%d)", strerror(err), err);

	/* stop capturing */
	if (gl_capture->flags & GL_CAPTURE_CAPTURING)
		gl_capture_stop(gl_capture);

	/* cancel glc */
	glc_state_set(gl_capture->glc, GLC_STATE_CANCEL);
	if (gl_capture->to)
		ps_buffer_cancel(gl_capture->to);
}
Example #6
0
int unpack_read_callback(glc_thread_state_t *state)
{
	unpack_t unpack = (unpack_t) state->ptr;

	if (state->header.type == GLC_MESSAGE_LZO) {
#ifdef __LZO
		state->write_size = ((glc_lzo_header_t *) state->read_data)->size;
		return 0;
#else
		glc_log(unpack->glc,
			 GLC_ERROR, "unpack", "LZO not supported");
		return ENOTSUP;
#endif
	} else if (state->header.type == GLC_MESSAGE_QUICKLZ) {
#ifdef __QUICKLZ
		state->write_size = ((glc_quicklz_header_t *) state->read_data)->size;
		return 0;
#else
		glc_log(unpack->glc,
			 GLC_ERROR, "unpack", "QuickLZ not supported");
		return ENOTSUP;
#endif
	} else if (state->header.type == GLC_MESSAGE_LZJB) {
#ifdef __LZJB
		state->write_size = ((glc_lzjb_header_t *) state->read_data)->size;
		return 0;
#else
		glc_log(unpack->glc,
			GLC_ERROR, "unpack", "LZJB not supported");
		return ENOTSUP;
#endif
	}
	__sync_fetch_and_add(&unpack->stats.pack_size, state->read_size);
	__sync_fetch_and_add(&unpack->stats.unpack_size, state->read_size);
	state->flags |= GLC_THREAD_COPY;
	return 0;
}
Example #7
0
File: x11.c Project: 7max/glc
int x11_init(glc_t *glc)
{
	x11.glc = glc;

	get_real_x11();

	if (getenv("GLC_HOTKEY")) {
		if (x11_parse_key(getenv("GLC_HOTKEY"), &x11.capture_key, &x11.capture_key_mask)) {
			glc_log(x11.glc, GLC_WARNING, "x11",
				 "invalid hotkey '%s'", getenv("GLC_HOTKEY"));
			glc_log(x11.glc, GLC_WARNING, "x11",
				 "using default <Shift>F8\n");
			x11.capture_key_mask = X11_KEY_SHIFT;
			x11.capture_key = XK_F8;
		}
	} else {
		x11.capture_key_mask = X11_KEY_SHIFT;
		x11.capture_key = XK_F8;
	}

	if (getenv("GLC_RELOAD_HOTKEY")) {
		if (x11_parse_key(getenv("GLC_RELOAD_HOTKEY"), &x11.reload_key, &x11.reload_key_mask)) {
			glc_log(x11.glc, GLC_WARNING, "x11",
				 "invalid reload hotkey '%s'", getenv("GLC_HOTKEY"));
			glc_log(x11.glc, GLC_WARNING, "x11",
				 "using default <Shift>F9\n");
			x11.reload_key_mask = X11_KEY_SHIFT;
			x11.reload_key = XK_F9;
		}
	} else {
		x11.reload_key_mask = X11_KEY_SHIFT;
		x11.reload_key = XK_F9;
	}

	return 0;
}
Example #8
0
/*
 * Might be called from signal handlers.
 */
int alsa_hook_writen(alsa_hook_t alsa_hook, snd_pcm_t *pcm,
		     void **bufs, snd_pcm_uframes_t size)
{
	struct alsa_hook_stream_s *stream;
	int c, ret = 0;
	int savedErrno = errno;

	if (!(alsa_hook->flags & ALSA_HOOK_CAPTURING))
		goto leave;

	alsa_hook_get_stream(alsa_hook, pcm, &stream);

	if (unlikely(!stream->initialized)) {
		ret = EINVAL;
		goto leave;
	}

	if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream))))
		goto leave;

	if (unlikely(stream->flags & GLC_AUDIO_INTERLEAVED)) {
		if (!(stream->mode & SND_PCM_ASYNC))
			glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook",
				 "stream format (interleaved) incompatible with snd_pcm_writen()");
		ret = EINVAL;
		goto unlock;
	}

	if (unlikely((ret = alsa_hook_wait_for_thread(alsa_hook, stream))))
		goto unlock;

	if (unlikely((ret = alsa_hook_set_data_size(stream,
				snd_pcm_frames_to_bytes(pcm, size)))))
		goto unlock;

	stream->capture_time = glc_state_time(alsa_hook->glc);
	for (c = 0; c < stream->channels; c++)
		memcpy(&stream->capture_data[c * snd_pcm_samples_to_bytes(pcm, size)], bufs[c],
		       snd_pcm_samples_to_bytes(pcm, size));

	sem_post(&stream->capture_full);

unlock:
	alsa_hook_unlock_write(alsa_hook, stream);
leave:
	errno = savedErrno;
	return ret;
}
Example #9
0
File: rgb.c Project: 7max/glc
void rgb_finish_callback(void *ptr, int err)
{
	rgb_t rgb = (rgb_t) ptr;
	struct rgb_video_stream_s *del;

	if (err)
		glc_log(rgb->glc, GLC_ERROR, "rgb", "%s (%d)", strerror(err), err);

	while (rgb->ctx != NULL) {
		del = rgb->ctx;
		rgb->ctx = rgb->ctx->next;

		pthread_rwlock_destroy(&del->update);
		free(del);
	}
}
Example #10
0
int alsa_hook_open(alsa_hook_t alsa_hook, snd_pcm_t *pcm, const char *name,
			 snd_pcm_stream_t pcm_stream, int mode)
{
	struct alsa_hook_stream_s *stream;

	alsa_hook_get_stream(alsa_hook, pcm, &stream);

	stream->mode = mode;

	glc_log(alsa_hook->glc, GLC_INFO, "alsa_hook",
		 "%p: opened device \"%s\" with mode is 0x%02x (async=%s, nonblock=%s)",
		 stream->pcm, name, mode,
		 mode & SND_PCM_ASYNC ? "yes" : "no",
		 mode & SND_PCM_NONBLOCK ? "yes" : "no");

	return 0;
}
Example #11
0
int alsa_hook_realloc_capture_buf(struct alsa_hook_stream_s *stream, ssize_t size)
{
	char *op = stream->capture_data;
	int ret = 0;
	stream->capture_data_size = size;
	stream->capture_data = (char *) realloc(stream->capture_data,
						stream->capture_data_size);

	if (unlikely(!stream->capture_data)) {
		glc_log(stream->alsa_hook->glc, GLC_ERROR, "alsa_hook",
			"realloc() error");
		free(op);
		stream->capture_data_size = 0;
		ret = ENOMEM;
	}
	return ret;
}
Example #12
0
int gl_capture_calc_geometry(gl_capture_t gl_capture, struct gl_capture_video_stream_s *video,
			     unsigned int w, unsigned int h)
{
	video->w = w;
	video->h = h;

	/* calculate image area when cropping */
	if (gl_capture->flags & GL_CAPTURE_CROP) {
		if (gl_capture->crop_x > video->w)
			video->cx = 0;
		else
			video->cx = gl_capture->crop_x;

		if (gl_capture->crop_y > video->h)
			video->cy = 0;
		else
			video->cy = gl_capture->crop_y;

		if (gl_capture->crop_w + video->cx > video->w)
			video->cw = video->w - video->cx;
		else
			video->cw = gl_capture->crop_w;

		if (gl_capture->crop_h + video->cy > video->h)
			video->ch = video->h - video->cy;
		else
			video->ch = gl_capture->crop_h;

		/* we need to recalc y coord for OpenGL */
		video->cy = video->h - video->ch - video->cy;
	} else {
		video->cw = video->w;
		video->ch = video->h;
		video->cx = video->cy = 0;
	}

	glc_log(gl_capture->glc, GLC_DEBUG, "gl_capture",
		 "calculated capture area for video %d is %ux%u+%u+%u",
		 video->id, video->cw, video->ch, video->cx, video->cy);

	video->row = video->cw * gl_capture->bpp;
	if (unlikely(video->row % gl_capture->pack_alignment != 0))
		video->row += gl_capture->pack_alignment -
			      video->row % gl_capture->pack_alignment;
	return 0;
}
Example #13
0
int alsa_hook_stream_init(alsa_hook_t alsa_hook,
			struct alsa_hook_stream_s *stream)
{
	int ret;
	glc_message_header_t msg_hdr;
	glc_audio_format_message_t fmt_msg;

	if (unlikely(!stream->fmt))
		return EINVAL;

	/* we need proper id for the stream */
	if (stream->id < 1)
		glc_state_audio_new(alsa_hook->glc, &stream->id,
					&stream->state_audio);

	glc_log(alsa_hook->glc, GLC_INFO, "alsa_hook",
		 "%p: initializing stream %d", stream->pcm, stream->id);

	/* init packet */
	if (stream->initialized)
		ps_packet_destroy(&stream->packet);
	ps_packet_init(&stream->packet, alsa_hook->to);

	/* prepare audio format message */
	msg_hdr.type = GLC_MESSAGE_AUDIO_FORMAT;
	fmt_msg.id = stream->id;
	fmt_msg.flags = stream->flags;
	fmt_msg.rate = stream->rate;
	fmt_msg.channels = stream->channels;
	fmt_msg.format = stream->format;
	ps_packet_open(&stream->packet, PS_PACKET_WRITE);
	ps_packet_write(&stream->packet, &msg_hdr,
			sizeof(glc_message_header_t));
	ps_packet_write(&stream->packet, &fmt_msg,
			sizeof(glc_audio_format_message_t));
	ps_packet_close(&stream->packet);

	alsa_hook_stream_wait(stream);

	ret = glc_simple_thread_create(alsa_hook->glc, &stream->thread,
				alsa_hook_thread, stream);

	stream->initialized = 1;
	return ret;
}
Example #14
0
int pack_process_start(pack_t pack, ps_buffer_t *from, ps_buffer_t *to)
{
	int ret;
	if (unlikely(pack->running))
		return EAGAIN;

	if (unlikely(!pack->compression)) {
		glc_log(pack->glc, GLC_ERROR, "pack",
			"attempt to start pack before setting the compression");
		return EINVAL;
	}

	if (unlikely((ret = glc_thread_create(pack->glc, &pack->thread, from, to))))
		return ret;
	pack->running = 1;

	return 0;
}
Example #15
0
int gl_capture_create_pbo(gl_capture_t gl_capture, struct gl_capture_video_stream_s *video)
{
	GLint binding;

	glc_log(gl_capture->glc, GLC_DEBUG, "gl_capture", "creating PBO");

	glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING_ARB, &binding);
	glPushAttrib(GL_ALL_ATTRIB_BITS);

	gl_capture->glGenBuffers(1, &video->pbo);
	gl_capture->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, video->pbo);
	gl_capture->glBufferData(GL_PIXEL_PACK_BUFFER_ARB, video->row * video->ch,
				NULL, GL_STREAM_READ);

	glPopAttrib();
	gl_capture->glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, binding);
	return 0;
}
Example #16
0
int pack_set_compression(pack_t pack, int compression)
{
	if (unlikely(pack->running))
		return EALREADY;

	if (compression == PACK_QUICKLZ) {
#ifdef __QUICKLZ
		pack->thread.write_callback = &pack_quicklz_write_callback;
		glc_log(pack->glc, GLC_INFO, "pack",
			 "compressing using QuickLZ");
#else
		glc_log(pack->glc, GLC_ERROR, "pack",
			 "QuickLZ not supported");
		return ENOTSUP;
#endif
	} else if (compression == PACK_LZO) {
#ifdef __LZO
		pack->thread.write_callback = &pack_lzo_write_callback;
		glc_log(pack->glc, GLC_INFO, "pack",
			 "compressing using LZO");
		lzo_init();
#else
		glc_log(pack->glc, GLC_ERROR, "pack",
			 "LZO not supported");
		return ENOTSUP;
#endif
	} else if (compression == PACK_LZJB) {
#ifdef __LZJB
		pack->thread.write_callback = &pack_lzjb_write_callback;
		glc_log(pack->glc, GLC_INFO, "pack",
			"compressing using LZJB");
#else
		glc_log(pack->glc, GLC_ERROR, "pack",
			"LZJB not supported");
		return ENOTSUP;
#endif
	} else {
		glc_log(pack->glc, GLC_ERROR, "pack",
			 "unknown/unsupported compression algorithm 0x%02x",
			 compression);
		return ENOTSUP;
	}

	pack->compression = compression;
	return 0;
}
Example #17
0
void alsa_play_finish_callback(void *priv, int err)
{
	alsa_play_t alsa_play = (alsa_play_t) priv;

	if (err)
		glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "%s (%d)",
			 strerror(err), err);

	if (alsa_play->pcm) {
		snd_pcm_drain(alsa_play->pcm);
		snd_pcm_close(alsa_play->pcm);
		alsa_play->pcm = NULL;
	}

	if (alsa_play->bufs) {
		free(alsa_play->bufs);
		alsa_play->bufs = NULL;
	}
}
Example #18
0
void ycbcr_finish_callback(void *ptr, int err)
{
	ycbcr_t ycbcr = ptr;
	struct ycbcr_video_stream_s *del;

	if (unlikely(err))
		glc_log(ycbcr->glc, GLC_ERROR, "ycbcr", "%s (%d)", strerror(err), err);

	while (ycbcr->video != NULL) {
		del = ycbcr->video;
		ycbcr->video = ycbcr->video->next;

		free(del->pos);
		free(del->factor);

		pthread_rwlock_destroy(&del->update);
		free(del);
	}
}
Example #19
0
File: rgb.c Project: 7max/glc
int rgb_init_lookup(rgb_t rgb)
{
	unsigned int Y, Cb, Cr, color;
	size_t lookup_size = (1 << LOOKUP_BITS) * (1 << LOOKUP_BITS) * (1 << LOOKUP_BITS) * 3;

	glc_log(rgb->glc, GLC_INFORMATION, "rgb",
		 "using %d bit lookup table (%zd bytes)", LOOKUP_BITS, lookup_size);
	rgb->lookup_table = malloc(lookup_size);

	color = 0;
	for (Y = 0; Y < 256; Y += (1 << (8 - LOOKUP_BITS))) {
		for (Cb = 0; Cb < 256; Cb += (1 << (8 - LOOKUP_BITS))) {
			for (Cr = 0; Cr < 256; Cr += (1 << (8 - LOOKUP_BITS))) {
				rgb->lookup_table[color + 0] = YCbCrJPEG_TO_RGB_Rd(Y, Cb, Cr);
				rgb->lookup_table[color + 1] = YCbCrJPEG_TO_RGB_Gd(Y, Cb, Cr);
				rgb->lookup_table[color + 2] = YCbCrJPEG_TO_RGB_Bd(Y, Cb, Cr);
				color += 3;
			}
		}
	}
	return 0;
}
Example #20
0
File: util.c Project: lano1106/glcs
int glc_util_set_pipe_size(glc_t *glc, int fd, int size)
{
	int pipe_max_size;
	int ret = 0;
	int cursize;

	if (unlikely(!size))
		return EINVAL;

	FILE *pms_file = fopen(PMS_FILE, "r");
	if (unlikely(!pms_file)) {
		glc_log(glc, GLC_ERROR, "util",
			"failed to open '%s", PMS_FILE);
		return 1;
	}
	if (unlikely(fscanf(pms_file,"%d", &pipe_max_size) != 1)) {
		glc_log(glc, GLC_ERROR, "util", "error reading '%s'",
			PMS_FILE);
		goto nocheck;
	}
	if (unlikely(size > pipe_max_size))
	{
		glc_log(glc, GLC_WARN, "util",
			"requested pipe buffer size of %d bytes "
			"exceeding system maximum value of %d."
			" Consider increase it by doing 'echo %d > %s'",
			size, pipe_max_size, size, PMS_FILE);
		size = pipe_max_size;
	}
nocheck:
	if (unlikely((cursize = fcntl(fd, F_GETPIPE_SZ)) < 0)) {
		glc_log(glc, GLC_WARN, "util",
			"error getting the pipe buffer current size: %s (%d)",
			strerror(errno), errno);
		cursize = 0;
	}
	glc_log(glc, GLC_DEBUG, "util", "pipe buffer size: current %d requested %d",
		cursize, size);
	if (size > cursize)
		if (unlikely((ret = fcntl(fd, F_SETPIPE_SZ, size)) < 0))
			glc_log(glc, GLC_ERROR, "util",
				"error setting the pipe buffer size: %s (%d)",
				strerror(errno), errno);
	fclose(pms_file);
	return ret;
}
Example #21
0
int pack_init(pack_t *pack, glc_t *glc)
{
#if !defined(__QUICKLZ) && !defined(__LZO) && !defined(__LZJB)
	glc_log(glc, GLC_ERROR, "pack",
		 "no supported compression algorithms found");
	return ENOTSUP;
#else
	*pack = (pack_t) calloc(1, sizeof(struct pack_s));

	(*pack)->glc = glc;
	(*pack)->compress_min = 1024;

	(*pack)->thread.flags = GLC_THREAD_WRITE | GLC_THREAD_READ;
	(*pack)->thread.ptr = *pack;
	(*pack)->thread.thread_create_callback = &pack_thread_create_callback;
	(*pack)->thread.thread_finish_callback = &pack_thread_finish_callback;
	(*pack)->thread.read_callback = &pack_read_callback;
	(*pack)->thread.finish_callback = &pack_finish_callback;
	(*pack)->thread.threads = glc_threads_hint(glc);

	return 0;
#endif
}
Example #22
0
/*
 * Might be called from signal handlers.
 */
int alsa_hook_mmap_commit(alsa_hook_t alsa_hook, snd_pcm_t *pcm,
				snd_pcm_uframes_t offset, snd_pcm_uframes_t frames)
{
	struct alsa_hook_stream_s *stream;
	unsigned int c;
	int ret = 0;
	int savedErrno = errno;

	if (!(alsa_hook->flags & ALSA_HOOK_CAPTURING))
		goto leave;

	alsa_hook_get_stream(alsa_hook, pcm, &stream);

	if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream))))
		goto leave;

	if (unlikely(stream->channels == 0))
		goto unlock; /* 0 channels :P */

	if (unlikely(!stream->mmap_areas)) {
		/* this might actually happen */
		if (!(stream->mode & SND_PCM_ASYNC))
			glc_log(alsa_hook->glc, GLC_WARN, "alsa_hook",
				 "snd_pcm_mmap_commit() before snd_pcm_mmap_begin()");
		goto unlock;
	}

	if (unlikely(offset != stream->offset))
		if (!(stream->mode & SND_PCM_ASYNC))
			glc_log(alsa_hook->glc, GLC_WARN, "alsa_hook",
				 "offset=%lu != stream->offset=%lu", offset, stream->offset);

	if (unlikely((ret = alsa_hook_wait_for_thread(alsa_hook, stream))))
		goto unlock;

	if (unlikely((ret = alsa_hook_set_data_size(stream,
			snd_pcm_frames_to_bytes(pcm, frames)))))
		goto unlock;

	stream->capture_time = glc_state_time(alsa_hook->glc);

	if (stream->flags & GLC_AUDIO_INTERLEAVED) {
		memcpy(stream->capture_data,
		       alsa_hook_mmap_pos(stream->mmap_areas, offset),
		       stream->capture_size);
	} else if (stream->complex) {
		alsa_hook_complex_to_interleaved(stream, stream->mmap_areas, offset,
		                                  frames, stream->capture_data);
	} else {
		for (c = 0; c < stream->channels; c++)
			memcpy(&stream->capture_data[c * snd_pcm_samples_to_bytes(stream->pcm, frames)],
			       alsa_hook_mmap_pos(&stream->mmap_areas[c], offset),
			       snd_pcm_samples_to_bytes(stream->pcm, frames));
	}

	sem_post(&stream->capture_full);

unlock:
	alsa_hook_unlock_write(alsa_hook, stream);
leave:
	errno = savedErrno;
	return ret;
}
Example #23
0
int alsa_hook_hw_params(alsa_hook_t alsa_hook, snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
	struct alsa_hook_stream_s *stream;

	snd_pcm_format_t format;
	snd_pcm_uframes_t period_size;
	snd_pcm_access_t access;
	int dir, ret;

	alsa_hook_get_stream(alsa_hook, pcm, &stream);
	if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream))))
		return ret;

	glc_log(alsa_hook->glc, GLC_DEBUG, "alsa_hook",
		 "%p: creating/updating configuration for stream %d",
		 stream->pcm, stream->id);

	/* extract information */
	if (unlikely((ret = snd_pcm_hw_params_get_format(params, &format)) < 0))
		goto err;
	stream->flags = 0; /* zero flags */
	stream->format = pcm_fmt_to_glc_fmt(format);
	if (unlikely(!stream->format)) {
		glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook",
			"%p: unsupported audio format 0x%02x", stream->pcm, format);
		ret = ENOTSUP;
		goto err;
	}
	if (unlikely((ret = snd_pcm_hw_params_get_rate(params, &stream->rate, &dir)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_get_channels(params, &stream->channels)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_get_period_size(params, &period_size, NULL)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_get_access(params, &access)) < 0))
		goto err;
	if ((access == SND_PCM_ACCESS_RW_INTERLEAVED) ||
	    (access == SND_PCM_ACCESS_MMAP_INTERLEAVED))
		stream->flags |= GLC_AUDIO_INTERLEAVED;
	else if (access == SND_PCM_ACCESS_MMAP_COMPLEX) {
		stream->flags |= GLC_AUDIO_INTERLEAVED; /* convert to interleaved */
		stream->complex = 1; /* do conversion */
	} else {
		glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook",
			 "%p: unsupported access mode 0x%02x", stream->pcm, access);
		ret = ENOTSUP;
		goto err;
	}

	glc_log(alsa_hook->glc, GLC_DEBUG, "alsa_hook",
		 "%p: %d channels, rate %d, flags 0x%02x",
		 stream->pcm, stream->channels, stream->rate, stream->flags);

	stream->fmt = 1;
	if (alsa_hook->started) {
		if (unlikely((ret = alsa_hook_stream_init(alsa_hook, stream))))
			goto err;
	}

	alsa_hook_unlock_write(alsa_hook, stream);
	return 0;

err:
	glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook",
		 "%p: can't extract hardware configuration: %s (%d)",
		 stream->pcm, snd_strerror(ret), ret);

	alsa_hook_unlock_write(alsa_hook, stream);
	return ret;
}
Example #24
0
/**
 * \todo smaller map is sometimes possible, should inflict better
 *       cache utilization => implement
 */
int ycbcr_generate_map(ycbcr_t ycbcr, struct ycbcr_video_stream_s *video)
{
    size_t scale_maps_size;
    unsigned int tp, x, y, r;
    float d, ofx, ofy, fx0, fx1, fy0, fy1;

    scale_maps_size = video->yw * video->yh * 4 + video->cw * video->ch * 4;
    glc_log(ycbcr->glc, GLC_DEBUG, "ycbcr", "generating %zd + %zd byte scale map for video %d",
            scale_maps_size * sizeof(unsigned int), scale_maps_size * sizeof(float), video->id);

    if (video->pos)
        video->pos = (unsigned int *) realloc(video->pos, sizeof(unsigned int) * scale_maps_size);
    else
        video->pos = (unsigned int *) malloc(sizeof(unsigned int) * scale_maps_size);

    if (video->factor)
        video->factor = (float *) realloc(video->factor, sizeof(float) * scale_maps_size);
    else
        video->factor = (float *) malloc(sizeof(float) * scale_maps_size);

    /* Y' */
    /* NEVER trust CPU with fp mathematics :/ */
    r = 0;
    do {
        d = (float) (video->w - r++) / (float) video->yw;
        glc_log(ycbcr->glc, GLC_DEBUG, "ycbcr", "Y: d = %f", d);
    } while ((d * (float) (video->yh - 1) + 1.0 > video->h) |
             (d * (float) (video->yw - 1) + 1.0 > video->w));

    ofx = ofy = 0;

    for (y = 0; y < video->yh; y++) {
        for (x = 0; x < video->yw; x++) {
            tp = (x + y * video->yw) * 4;

            video->pos[tp + 0] = ((unsigned int) ofx + 0) * video->bpp +
                                 (video->h - 1 - (unsigned int) ofy) * video->row;
            video->pos[tp + 1] = ((unsigned int) ofx + 1) * video->bpp +
                                 (video->h - 1 - (unsigned int) ofy) * video->row;
            video->pos[tp + 2] = ((unsigned int) ofx + 0) * video->bpp +
                                 (video->h - 2 - (unsigned int) ofy) * video->row;
            video->pos[tp + 3] = ((unsigned int) ofx + 1) * video->bpp +
                                 (video->h - 2 - (unsigned int) ofy) * video->row;

            fx1 = (float) x * d - (float) ((unsigned int) ofx);
            fx0 = 1.0 - fx1;
            fy1 = (float) y * d - (float) ((unsigned int) ofy);
            fy0 = 1.0 - fy1;

            video->factor[tp + 0] = fx0 * fy0;
            video->factor[tp + 1] = fx1 * fy0;
            video->factor[tp + 2] = fx0 * fy1;
            video->factor[tp + 3] = fx1 * fy1;

            ofx += d;
        }
        ofy += d;
        ofx = 0;
    }

    /* CbCr */
    /* try to match Y */
    r = (r < 2) ? (0) : (r - 2);
    do {
        d = (float) (video->w - r++) / (float) video->cw;
        glc_log(ycbcr->glc, GLC_DEBUG, "ycbcr", "C: d = %f", d);
    } while ((d * (float) (video->ch - 1) + 1.0 > video->h) |
             (d * (float) (video->cw - 1) + 1.0 > video->w));

    ofx = ofy = 0;

    for (y = 0; y < video->ch; y++) {
        for (x = 0; x < video->cw; x++) {
            tp = (video->yw * video->yh * 4) + (x + y * video->cw) * 4;

            video->pos[tp + 0] = ((unsigned int) ofx + 0) * video->bpp +
                                 (video->h - 1 - (unsigned int) ofy) * video->row;
            video->pos[tp + 1] = ((unsigned int) ofx + 1) * video->bpp +
                                 (video->h - 1 - (unsigned int) ofy) * video->row;
            video->pos[tp + 2] = ((unsigned int) ofx + 0) * video->bpp +
                                 (video->h - 2 - (unsigned int) ofy) * video->row;
            video->pos[tp + 3] = ((unsigned int) ofx + 1) * video->bpp +
                                 (video->h - 2 - (unsigned int) ofy) * video->row;

            fx1 = (float) x * d - (float) ((unsigned int) ofx);
            fx0 = 1.0 - fx1;
            fy1 = (float) y * d - (float) ((unsigned int) ofy);
            fy0 = 1.0 - fy1;

            video->factor[tp + 0] = fx0 * fy0;
            video->factor[tp + 1] = fx1 * fy0;
            video->factor[tp + 2] = fx0 * fy1;
            video->factor[tp + 3] = fx1 * fy1;

            ofx += d;
        }
        ofy += d;
        ofx = 0;
    }

    return 0;
}
Example #25
0
int alsa_play_hw(alsa_play_t alsa_play, glc_audio_format_message_t *fmt_msg)
{
	snd_pcm_hw_params_t *hw_params = NULL;
	snd_pcm_access_t access;
	unsigned int period_time;
	unsigned int buffer_time;
	int dir = 0, ret = 0;

	if (unlikely(fmt_msg->id != alsa_play->id))
		return 0;

	alsa_play->flags    = fmt_msg->flags;
	alsa_play->format   = fmt_msg->format;
	alsa_play->rate     = fmt_msg->rate;
	alsa_play->channels = fmt_msg->channels;

	if (alsa_play->pcm) /* re-open */
		snd_pcm_close(alsa_play->pcm);

	if (alsa_play->flags & GLC_AUDIO_INTERLEAVED)
		access = SND_PCM_ACCESS_RW_INTERLEAVED;
	else
		access = SND_PCM_ACCESS_RW_NONINTERLEAVED;

	if (unlikely((ret = snd_pcm_open(&alsa_play->pcm, alsa_play->device,
				SND_PCM_STREAM_PLAYBACK, 0)) < 0))
		goto err;
	snd_pcm_hw_params_alloca(&hw_params);
	if (unlikely((ret = snd_pcm_hw_params_any(alsa_play->pcm, hw_params)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_set_access(alsa_play->pcm,
						hw_params, access)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_set_format(alsa_play->pcm, hw_params,
						glc_fmt_to_pcm_fmt(alsa_play->format))) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_set_channels(alsa_play->pcm, hw_params,
						  alsa_play->channels)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params_set_rate(alsa_play->pcm, hw_params,
					      alsa_play->rate, 0)) < 0))
		goto err;

	if (unlikely((ret = snd_pcm_hw_params_get_buffer_time_max(hw_params,
						&buffer_time, 0))))
		goto err;

	if (buffer_time > 1000000) {
		glc_log(alsa_play->glc, GLC_INFO, "alsa_play",
			"buffer time max is %u usec. We will limit it to 1 sec",
			buffer_time);
		buffer_time = 1000000;
	}

	period_time = buffer_time / 4;
	alsa_play->silence_threshold = period_time*2000;

	if (unlikely((ret = snd_pcm_hw_params_set_period_time_near(alsa_play->pcm,
		hw_params, &period_time, 0)) < 0))
		goto err;

	if (unlikely((ret = snd_pcm_hw_params_set_buffer_time_near(alsa_play->pcm,
		hw_params, &buffer_time, 0)) < 0))
		goto err;
	if (unlikely((ret = snd_pcm_hw_params(alsa_play->pcm, hw_params)) < 0))
		goto err;

	alsa_play->bufs = (void **) malloc(sizeof(void *) * alsa_play->channels);

	glc_log(alsa_play->glc, GLC_INFO, "alsa_play",
		"opened pcm %s for playback. buffer_time: %u",
		alsa_play->device, buffer_time);

	return 0;
err:
	glc_log(alsa_play->glc, GLC_ERROR, "alsa_play",
		"can't initialize pcm %s: %s (%d)",
		alsa_play->device, snd_strerror(ret), ret);
	return -ret;
}
Example #26
0
/*
 * multithreading notes:
 *
 * This function could be accessed concurrently for different video streams, with
 * the pair dpy,drawable identifying each stream.
 */
int gl_capture_frame(gl_capture_t gl_capture, Display *dpy, GLXDrawable drawable)
{
	struct gl_capture_video_stream_s *video;
	glc_message_header_t msg;
	glc_video_frame_header_t pic;
	glc_utime_t now;
	glc_utime_t before_capture,after_capture;
	char *dma;
	int ret = 0;

	if (!(gl_capture->flags & GL_CAPTURE_CAPTURING))
		return 0; /* capturing not active */

	gl_capture_get_video_stream(gl_capture, &video, dpy, drawable);

	/* get current time */
	if (unlikely(gl_capture->flags & GL_CAPTURE_IGNORE_TIME))
		now = video->last + gl_capture->fps;
	else
		now = glc_state_time(gl_capture->glc);

	/* has gl_capture->fps nanoseconds elapsed since last capture */
	if ((now - video->last < gl_capture->fps) &&
	    !(gl_capture->flags & GL_CAPTURE_LOCK_FPS) &&
	    !(gl_capture->flags & GL_CAPTURE_IGNORE_TIME))
		goto finish;

	/* not really needed until now */
	gl_capture_update_video_stream(gl_capture, video);

	/* if PBO is not active, just start transfer and finish */
	if (unlikely((gl_capture->flags & GL_CAPTURE_USE_PBO) &&
	     __sync_bool_compare_and_swap(&video->pbo_active,0,1))) {
		ret = gl_capture_start_pbo(gl_capture, video);
		video->pbo_time = now;
		goto finish;
	}

	if (unlikely(ps_packet_open(&video->packet,
				((gl_capture->flags & GL_CAPTURE_LOCK_FPS) ||
				(gl_capture->flags & GL_CAPTURE_IGNORE_TIME)) ?
				(PS_PACKET_WRITE) :
				(PS_PACKET_WRITE | PS_PACKET_TRY))))
		goto finish;

	if (unlikely((ret = ps_packet_setsize(&video->packet, video->row * video->ch
						+ sizeof(glc_message_header_t)
						+ sizeof(glc_video_frame_header_t)))))
		goto cancel;

	msg.type = GLC_MESSAGE_VIDEO_FRAME;
	if (unlikely((ret = ps_packet_write(&video->packet,
					    &msg, sizeof(glc_message_header_t)))))
		goto cancel;

	/*
	 * if we are using PBO we will actually write previous picture to buffer.
	 * Also, make sure that pbo_time is not in the future. This could happen if
	 * the state time is reset by reloading the capture between a pbo start
	 * and a pbo read.
	 */
	pic.time = (gl_capture->flags & GL_CAPTURE_USE_PBO &&
		    video->pbo_time < now)?video->pbo_time:now;
	pic.id   = video->id;
	if (unlikely((ret = ps_packet_write(&video->packet,
					    &pic, sizeof(glc_video_frame_header_t)))))
		goto cancel;

	if (video->gather_stats)
		before_capture = glc_state_time(gl_capture->glc);
	if (gl_capture->flags & GL_CAPTURE_USE_PBO) {
		if (unlikely((ret = gl_capture_read_pbo(gl_capture, video))))
			goto cancel;

		ret = gl_capture_start_pbo(gl_capture, video);
		video->pbo_time = now;
	} else {
		if (unlikely((ret = ps_packet_dma(&video->packet, (void *) &dma,
					video->row * video->ch, PS_ACCEPT_FAKE_DMA))))
			goto cancel;

		ret = gl_capture_get_pixels(gl_capture, video, dma);
	}
	if (video->gather_stats) {
		after_capture = glc_state_time(gl_capture->glc);
		video->capture_time_ns += after_capture - before_capture;
	}

	ps_packet_close(&video->packet);
	video->num_frames++;
	now = glc_state_time(gl_capture->glc);

	if (unlikely((gl_capture->flags & GL_CAPTURE_LOCK_FPS) &&
		    !(gl_capture->flags & GL_CAPTURE_IGNORE_TIME))) {
		if (now - video->last < gl_capture->fps) {
			struct timespec ts = { .tv_sec  = (gl_capture->fps + video->last - now)/1000000000,
					       .tv_nsec = (gl_capture->fps + video->last - now)%1000000000 };
			nanosleep(&ts,NULL);
		}
	}

	/* increment by 1/fps seconds */
	video->last += gl_capture->fps;

finish:
	if (unlikely(ret != 0))
		gl_capture_error(gl_capture, ret);

	if (gl_capture->flags & GL_CAPTURE_DRAW_INDICATOR)
		glCallList(video->indicator_list);

	return ret;
cancel:
	if (ret == EBUSY) {
		ret = 0;
		glc_log(gl_capture->glc, GLC_INFO, "gl_capture",
			 "dropped frame, buffer not ready");
	}
	ps_packet_cancel(&video->packet);
	goto finish;
}

int gl_capture_refresh_color_correction(gl_capture_t gl_capture)
{
	struct gl_capture_video_stream_s *video;

	if (unlikely(!(gl_capture->flags & GL_CAPTURE_CAPTURING)))
		return 0; /* capturing not active */

	glc_log(gl_capture->glc, GLC_INFO, "gl_capture",
		 "refreshing color correction");

	pthread_rwlock_rdlock(&gl_capture->videolist_lock);
	video = gl_capture->video;
	while (video != NULL) {
		gl_capture_update_color(gl_capture, video);
		video = video->next;
	}
	pthread_rwlock_unlock(&gl_capture->videolist_lock);

	return 0;
}

/** \todo support GammaRamp */
int gl_capture_update_color(gl_capture_t gl_capture, struct gl_capture_video_stream_s *video)
{
	glc_message_header_t msg_hdr;
	glc_color_message_t msg;
	XF86VidModeGamma gamma;
	int ret = 0;

	XF86VidModeGetGamma(video->dpy, video->screen, &gamma);

	if ((gamma.red == video->gamma_red) &&
	    (gamma.green == video->gamma_green) &&
	    (gamma.blue == video->gamma_blue))
		return 0; /* nothing to update */

	msg_hdr.type = GLC_MESSAGE_COLOR;
	msg.id = video->id;
	msg.red = gamma.red;
	msg.green = gamma.green;
	msg.blue = gamma.blue;

	/** \todo figure out brightness and contrast */
	msg.brightness = msg.contrast = 0;

	glc_log(gl_capture->glc, GLC_INFO, "gl_capture",
		 "color correction: brightness=%f, contrast=%f, red=%f, green=%f, blue=%f",
		 msg.brightness, msg.contrast, msg.red, msg.green, msg.blue);

	if (unlikely((ret = ps_packet_open(&video->packet, PS_PACKET_WRITE))))
		goto err;
	if (unlikely((ret = ps_packet_write(&video->packet,
				&msg_hdr, sizeof(glc_message_header_t)))))
		goto err;
	if (unlikely((ret = ps_packet_write(&video->packet,
				&msg, sizeof(glc_color_message_t)))))
		goto err;
	if (unlikely((ret = ps_packet_close(&video->packet))))
		goto err;

	return 0;

err:
	ps_packet_cancel(&video->packet);

	glc_log(gl_capture->glc, GLC_ERROR, "gl_capture",
		 "can't write gamma correction information to buffer: %s (%d)",
		 strerror(ret), ret);
	return ret;
}
Example #27
0
int gl_capture_destroy_pbo(gl_capture_t gl_capture, struct gl_capture_video_stream_s *video)
{
	glc_log(gl_capture->glc, GLC_DEBUG, "gl_capture", "destroying PBO");
	gl_capture->glDeleteBuffers(1, &video->pbo);
	return 0;
}
Example #28
0
int ycbcr_video_format_message(ycbcr_t ycbcr, glc_video_format_message_t *video_format)
{
    struct ycbcr_video_stream_s *video;

    ycbcr_get_video_stream(ycbcr, video_format->id, &video);
    pthread_rwlock_wrlock(&video->update);

    if (video_format->format == GLC_VIDEO_BGRA)
        video->bpp = 4;
    else if (video_format->format == GLC_VIDEO_BGR)
        video->bpp = 3;
    else {
        video->convert = NULL;
        pthread_rwlock_unlock(&video->update);
        return 0;
    }

    video->w = video_format->width;
    video->h = video_format->height;

    video->row = video->w * video->bpp;

    if (video_format->flags & GLC_VIDEO_DWORD_ALIGNED) {
        if (video->row % 8 != 0)
            video->row += 8 - video->row % 8;
    }

    video->scale = ycbcr->scale;
    video->yw = video->w * video->scale;
    video->yh = video->h * video->scale;
    video->yw -= video->yw % 2; /* safer and faster             */
    video->yh -= video->yh % 2; /* but we might drop a pixel... */

    video->cw = video->yw / 2;
    video->ch = video->yh / 2;

    /* nuke old flags */
    video_format->flags &= ~GLC_VIDEO_DWORD_ALIGNED;
    video_format->format = GLC_VIDEO_YCBCR_420JPEG;
    video_format->width = video->yw;
    video_format->height = video->yh;

    if (video->scale == 1.0)
        video->convert = &ycbcr_bgr_to_jpeg420;
    else if (video->scale == 0.5) {
        glc_log(ycbcr->glc, GLC_DEBUG, "ycbcr",
                "scaling to half-size (from %ux%u to %ux%u)",
                video->w, video->h, video->yw, video->yh);
        video->convert = &ycbcr_bgr_to_jpeg420_half;
    } else {
        glc_log(ycbcr->glc, GLC_DEBUG, "ycbcr",
                "scaling with factor %f (from %ux%u to %ux%u)",
                video->scale, video->w, video->h, video->yw, video->yh);
        video->convert = &ycbcr_bgr_to_jpeg420_scale;
        ycbcr_generate_map(ycbcr, video);
    }

    video->size = video->yw * video->yh + 2 * (video->cw * video->ch);

    pthread_rwlock_unlock(&video->update);
    return 0;
}
Example #29
0
File: opengl.c Project: 7max/glc
int opengl_init(glc_t *glc)
{
	opengl.glc = glc;
	opengl.buffer = opengl.unscaled = NULL;
	opengl.started = 0;
	opengl.scale_factor = 1.0;
	opengl.capture_glfinish = 0;
	opengl.read_buffer = GL_FRONT;
	opengl.capturing = 0;
	int ret = 0;
	unsigned int x, y, w, h;

	glc_log(opengl.glc, GLC_DEBUG, "opengl", "initializing");

	/* initialize gl_capture object */
	if ((ret = gl_capture_init(&opengl.gl_capture, opengl.glc)))
		return ret;

	/* load environment variables */
	opengl.fps = 30;
	if (getenv("GLC_FPS"))
		opengl.fps = atof(getenv("GLC_FPS"));
	glc_util_info_fps(opengl.glc, opengl.fps);
	gl_capture_set_fps(opengl.gl_capture, opengl.fps);

	if (getenv("GLC_COLORSPACE")) {
		if (!strcmp(getenv("GLC_COLORSPACE"), "420jpeg"))
			opengl.convert_ycbcr_420jpeg = 1;
		else if (!strcmp(getenv("GLC_COLORSPACE"), "bgr"))
			opengl.convert_ycbcr_420jpeg = 0;
		else
			glc_log(opengl.glc, GLC_WARNING, "opengl",
				 "unknown colorspace '%s'", getenv("GLC_COLORSPACE"));
	} else
		opengl.convert_ycbcr_420jpeg = 1;

	if (getenv("GLC_UNSCALED_BUFFER_SIZE"))
		opengl.unscaled_size = atoi(getenv("GLC_UNSCALED_BUFFER_SIZE")) * 1024 * 1024;
	else
		opengl.unscaled_size = 1024 * 1024 * 25;

	if (getenv("GLC_CAPTURE")) {
		if (!strcmp(getenv("GLC_CAPTURE"), "front"))
			opengl.read_buffer = GL_FRONT;
		else if (!strcmp(getenv("GLC_CAPTURE"), "back"))
			opengl.read_buffer = GL_BACK;
		else
			glc_log(opengl.glc, GLC_WARNING, "opengl",
				 "unknown capture buffer '%s'", getenv("GLC_CAPTURE"));
	}
	gl_capture_set_read_buffer(opengl.gl_capture, opengl.read_buffer);

	if (getenv("GLC_CAPTURE_GLFINISH"))
		opengl.capture_glfinish = atoi(getenv("GLC_CAPTURE_GLFINISH"));

	if (getenv("GLC_SCALE"))
		opengl.scale_factor = atof(getenv("GLC_SCALE"));

	if (getenv("GLC_TRY_PBO"))
		gl_capture_try_pbo(opengl.gl_capture, atoi(getenv("GLC_TRY_PBO")));

	gl_capture_set_pack_alignment(opengl.gl_capture, 8);
	if (getenv("GLC_CAPTURE_DWORD_ALIGNED")) {
		if (!atoi(getenv("GLC_CAPTURE_DWORD_ALIGNED")))
			gl_capture_set_pack_alignment(opengl.gl_capture, 1);
	}

	if (getenv("GLC_CROP")) {
		w = h = x = y = 0;

		/* we need at least 2 values, width and height */
		if (sscanf(getenv("GLC_CROP"), "%ux%u+%u+%u",
			   &w, &h, &x, &y) >= 2)
			gl_capture_crop(opengl.gl_capture, x, y, w, h);
	}

	gl_capture_draw_indicator(opengl.gl_capture, 0);
	if (getenv("GLC_INDICATOR"))
		gl_capture_draw_indicator(opengl.gl_capture, atoi(getenv("GLC_INDICATOR")));

	gl_capture_lock_fps(opengl.gl_capture, 0);
	if (getenv("GLC_LOCK_FPS"))
		gl_capture_lock_fps(opengl.gl_capture, atoi(getenv("GLC_LOCK_FPS")));

	get_real_opengl();
	return 0;
}
Example #30
0
int alsa_play_play(alsa_play_t alsa_play, glc_audio_data_header_t *audio_hdr, char *data)
{
	snd_pcm_uframes_t frames, rem;
	snd_pcm_sframes_t ret = 0;
	unsigned int c;

	if (audio_hdr->id != alsa_play->id)
		return 0;

	if (!alsa_play->pcm) {
		glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "broken stream %d",
			 alsa_play->id);
		return EINVAL;
	}

	frames = snd_pcm_bytes_to_frames(alsa_play->pcm, audio_hdr->size);
	glc_utime_t time = glc_state_time(alsa_play->glc);
	glc_utime_t duration = ((glc_utime_t) 1000000000 * (glc_utime_t) frames) /
			       (glc_utime_t) alsa_play->rate;

	if (time + alsa_play->silence_threshold + duration < audio_hdr->time) {
		struct timespec ts = {
		.tv_sec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)/1000000000,
		.tv_nsec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)%1000000000 };
		nanosleep(&ts,NULL);
	}
	/*
	 * This condition determine what will be the initial audio packet.
	 * it is preferable to be ahead by < duration/2 than behind
	 * the video by > duration/2
	 */
	else if (time > audio_hdr->time + duration/2) {
		glc_log(alsa_play->glc, GLC_DEBUG, "alsa_play",
			"dropped packet. now %" PRId64 " ts %" PRId64,
			time, audio_hdr->time);
		return 0;
	}

	rem = frames;

	while (rem > 0) {
		/* alsa is horrible... */
		/*snd_pcm_wait(alsa_play->pcm, duration);*/

		if (alsa_play->flags & GLC_AUDIO_INTERLEAVED)
			ret = snd_pcm_writei(alsa_play->pcm,
					    &data[snd_pcm_frames_to_bytes(alsa_play->pcm, frames - rem)],
					    rem);
		else {
			for (c = 0; c < alsa_play->channels; c++)
				alsa_play->bufs[c] =
					&data[snd_pcm_samples_to_bytes(alsa_play->pcm, frames)
					      * c + snd_pcm_samples_to_bytes(alsa_play->pcm, frames - rem)];
			ret = snd_pcm_writen(alsa_play->pcm, alsa_play->bufs, rem);
		}

		if (ret == 0)
			break;

		if ((ret == -EBUSY) || (ret == -EAGAIN))
			break;
		else if (ret < 0) {
			if ((ret = alsa_play_xrun(alsa_play, ret))) {
				glc_log(alsa_play->glc, GLC_ERROR, "alsa_play",
					 "xrun recovery failed: %s", snd_strerror(-ret));
				return ret;
			}
		} else
			rem -= ret;
	}

	return 0;
}