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; }
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); }
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; }
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; }
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); }
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; }
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; }
/* * 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; }
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); } }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; } }
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); } }
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; }
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; }
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 }
/* * 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; }
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; }
/** * \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; }
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; }
/* * 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; }
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; }
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; }
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; }
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; }