/* * The purpose of this thread is to make this module async signal safe. * ie: it couldn't be called safely from a sighandler if host process * use ALSA async mode and write to ALSA API from a sighandler. */ void *alsa_hook_thread(void *argptr) { struct alsa_hook_stream_s *stream = (struct alsa_hook_stream_s *) argptr; glc_audio_data_header_t hdr; glc_message_header_t msg_hdr; int ret = 0; msg_hdr.type = GLC_MESSAGE_AUDIO_DATA; hdr.id = stream->id; stream->capture_ready = 1; while (1) { sem_wait(&stream->capture_full); stream->capture_ready = 0; if (unlikely(!stream->thread.running)) break; if (unlikely(stream->capture_size < 0)) { alsa_hook_realloc_capture_buf(stream,-stream->capture_size); goto capture_ready; } hdr.time = stream->capture_time; hdr.size = stream->capture_size; if (unlikely((ret = ps_packet_open(&stream->packet, PS_PACKET_WRITE)))) break; if (unlikely((ret = ps_packet_setsize(&stream->packet, hdr.size + sizeof(glc_message_header_t) + sizeof(glc_audio_data_header_t))))) break; if (unlikely((ret = ps_packet_write(&stream->packet, &msg_hdr, sizeof(glc_message_header_t))))) break; if (unlikely((ret = ps_packet_write(&stream->packet, &hdr, sizeof(glc_audio_data_header_t))))) break; if (unlikely((ret = ps_packet_write(&stream->packet, stream->capture_data, hdr.size)))) break; if (unlikely((ret = ps_packet_close(&stream->packet)))) break; if (!(stream->mode & SND_PCM_ASYNC)) sem_post(&stream->capture_empty); capture_ready: stream->capture_ready = 1; } if (ret != 0) glc_log(stream->alsa_hook->glc, GLC_ERROR, "alsa_hook", "thread failed: %s (%d)", strerror(ret), ret); return NULL; }
/* * 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; }