static void ffemu_flush_buffers(ffemu_t *handle) { void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size); size_t audio_buf_size = handle->config.audio_enable ? (handle->audio.codec->frame_size * handle->params.channels * sizeof(int16_t)) : 0; void *audio_buf = audio_buf_size ? av_malloc(audio_buf_size) : NULL; // Try pushing data in an interleaving pattern to ease the work of the muxer a bit. bool did_work; do { did_work = false; if (handle->config.audio_enable) { if (fifo_read_avail(handle->audio_fifo) >= audio_buf_size) { fifo_read(handle->audio_fifo, audio_buf, audio_buf_size); struct ffemu_audio_data aud = {0}; aud.frames = handle->audio.codec->frame_size; aud.data = audio_buf; ffemu_push_audio_thread(handle, &aud, true); did_work = true; } } struct ffemu_video_data attr_buf; if (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf)) { fifo_read(handle->attr_fifo, &attr_buf, sizeof(attr_buf)); fifo_read(handle->video_fifo, video_buf, attr_buf.height * attr_buf.pitch); attr_buf.data = video_buf; ffemu_push_video_thread(handle, &attr_buf); did_work = true; } } while (did_work); // Flush out last audio. if (handle->config.audio_enable) ffemu_flush_audio(handle, audio_buf, audio_buf_size); // Flush out last video. ffemu_flush_video(handle); av_free(video_buf); av_free(audio_buf); }
static void ffemu_flush_audio(ffemu_t *handle, int16_t *audio_buf, size_t audio_buf_size) { size_t avail = fifo_read_avail(handle->audio_fifo); if (avail) { fifo_read(handle->audio_fifo, audio_buf, avail); struct ffemu_audio_data aud = {0}; aud.frames = avail / (sizeof(int16_t) * handle->params.channels); aud.data = audio_buf; ffemu_push_audio_thread(handle, &aud, false); } for (;;) { AVPacket pkt; if (!encode_audio(handle, &pkt, true) || !pkt.size || av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0) break; } }
static void ffemu_thread(void *data) { ffemu_t *ff = (ffemu_t*)data; // For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size); assert(video_buf); size_t audio_buf_size = 512 * ff->params.channels * sizeof(int16_t); int16_t *audio_buf = (int16_t*)av_malloc(audio_buf_size); assert(audio_buf); while (ff->alive) { struct ffemu_video_data attr_buf; bool avail_video = false; bool avail_audio = false; slock_lock(ff->lock); if (fifo_read_avail(ff->attr_fifo) >= sizeof(attr_buf)) avail_video = true; if (fifo_read_avail(ff->audio_fifo) >= audio_buf_size) avail_audio = true; slock_unlock(ff->lock); if (!avail_video && !avail_audio) { slock_lock(ff->cond_lock); if (ff->can_sleep) { ff->can_sleep = false; scond_wait(ff->cond, ff->cond_lock); ff->can_sleep = true; } else scond_signal(ff->cond); slock_unlock(ff->cond_lock); } if (avail_video) { slock_lock(ff->lock); fifo_read(ff->attr_fifo, &attr_buf, sizeof(attr_buf)); fifo_read(ff->video_fifo, video_buf, attr_buf.height * attr_buf.pitch); slock_unlock(ff->lock); scond_signal(ff->cond); attr_buf.data = video_buf; ffemu_push_video_thread(ff, &attr_buf); } if (avail_audio) { slock_lock(ff->lock); fifo_read(ff->audio_fifo, audio_buf, audio_buf_size); slock_unlock(ff->lock); scond_signal(ff->cond); struct ffemu_audio_data aud = {0}; aud.frames = 512; aud.data = audio_buf; ffemu_push_audio_thread(ff, &aud, true); } } av_free(video_buf); av_free(audio_buf); }