int audio_capture_data(audio_capture_t audio_capture, void *data, size_t size) { glc_message_header_t msg_hdr; glc_audio_data_header_t audio_hdr; int ret; if (!(audio_capture->flags & AUDIO_CAPTURE_CAPTURING)) return 0; if (audio_capture->flags & AUDIO_CAPTURE_CFG_CHANGED) { if ((ret = audio_capture_write_cfg(audio_capture))) return ret; audio_capture->flags &= ~AUDIO_CAPTURE_CFG_CHANGED; } if (!(audio_capture->flags & AUDIO_CAPTURE_IGNORE_TIME)) audio_capture->time = glc_state_time(audio_capture->glc); msg_hdr.type = GLC_MESSAGE_AUDIO_DATA; audio_hdr.id = audio_capture->id; /* should be set to valid one */ audio_hdr.size = size; audio_hdr.time = audio_capture->time; if (audio_capture->flags & AUDIO_CAPTURE_IGNORE_TIME) audio_capture->time += ((glc_utime_t) size * (glc_utime_t) 1000000) / (glc_utime_t) (audio_capture_frames_to_bytes(audio_capture, 1) * audio_capture->rate); if ((ret = ps_packet_open(&audio_capture->packet, PS_PACKET_WRITE))) goto err; if ((ret = ps_packet_write(&audio_capture->packet, &msg_hdr, sizeof(glc_message_header_t)))) goto err; if ((ret = ps_packet_write(&audio_capture->packet, &audio_hdr, sizeof(glc_audio_data_header_t)))) goto err; if ((ret = ps_packet_write(&audio_capture->packet, data, size))) goto err; if ((ret = ps_packet_close(&audio_capture->packet))) goto err; return 0; err: ps_buffer_cancel(audio_capture->target); glc_state_set(audio_capture->glc, GLC_STATE_CANCEL); glc_log(audio_capture->glc, GLC_ERROR, "audio_capture", "can't write audio data to buffer"); glc_log(audio_capture->glc, GLC_ERROR, "audio_capture", "%s (%d)", strerror(ret), ret); return ret; }
/* * 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; }
/* * Might be called from signal handlers. */ int alsa_hook_writei(alsa_hook_t alsa_hook, snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) { struct alsa_hook_stream_s *stream; 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(!stream->initialized)) { ret = EINVAL; goto leave; } if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream)))) goto leave; 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); memcpy(stream->capture_data, buffer, stream->capture_size); sem_post(&stream->capture_full); unlock: alsa_hook_unlock_write(alsa_hook, stream); leave: errno = savedErrno; 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 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; }
/* * 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; }