Heyhey :D
This commit is contained in:
parent
f761811189
commit
54bc0a8d96
243
record/ffemu.c
243
record/ffemu.c
|
@ -11,6 +11,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "ffemu.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include "SDL_thread.h"
|
||||
|
||||
struct video_info
|
||||
{
|
||||
|
@ -57,13 +59,23 @@ struct ffemu
|
|||
struct muxer_info muxer;
|
||||
|
||||
struct ffemu_params params;
|
||||
|
||||
SDL_cond *cond;
|
||||
SDL_mutex *cond_lock;
|
||||
SDL_mutex *lock;
|
||||
fifo_buffer_t *audio_fifo;
|
||||
fifo_buffer_t *video_fifo;
|
||||
fifo_buffer_t *attr_fifo;
|
||||
SDL_Thread *thread;
|
||||
volatile bool alive;
|
||||
volatile bool can_sleep;
|
||||
};
|
||||
|
||||
static int init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
{
|
||||
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FLAC);
|
||||
if (!codec)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
audio->codec = avcodec_alloc_context();
|
||||
avcodec_get_context_defaults(audio->codec);
|
||||
|
@ -73,25 +85,25 @@ static int init_audio(struct audio_info *audio, struct ffemu_params *param)
|
|||
audio->codec->channels = param->channels;
|
||||
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
if (avcodec_open(audio->codec, codec) != 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t));
|
||||
if (!audio->buffer)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
audio->outbuf_size = 200000;
|
||||
audio->outbuf = av_malloc(audio->outbuf_size);
|
||||
if (!audio->outbuf)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init_video(struct video_info *video, struct ffemu_params *param)
|
||||
static bool init_video(struct video_info *video, struct ffemu_params *param)
|
||||
{
|
||||
AVCodec *codec = avcodec_find_encoder(CODEC_ID_HUFFYUV);
|
||||
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FFV1);
|
||||
if (!codec)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
video->codec = avcodec_alloc_context();
|
||||
video->codec->width = param->out_width;
|
||||
|
@ -101,7 +113,7 @@ static int init_video(struct video_info *video, struct ffemu_params *param)
|
|||
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
|
||||
|
||||
if (avcodec_open(video->codec, codec) != 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
|
||||
video->outbuf_size = 5000000;
|
||||
|
@ -113,10 +125,10 @@ static int init_video(struct video_info *video, struct ffemu_params *param)
|
|||
video->conv_frame = avcodec_alloc_frame();
|
||||
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_RGB32, 512, 448);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init_muxer(ffemu_t *handle)
|
||||
static bool init_muxer(ffemu_t *handle)
|
||||
{
|
||||
AVFormatContext *ctx = avformat_alloc_context();
|
||||
av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename));
|
||||
|
@ -124,7 +136,7 @@ static int init_muxer(ffemu_t *handle)
|
|||
if (avio_open(&ctx->pb, ctx->filename, URL_WRONLY) < 0)
|
||||
{
|
||||
av_free(ctx);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
int stream_cnt = 0;
|
||||
|
@ -144,10 +156,53 @@ static int init_muxer(ffemu_t *handle)
|
|||
handle->muxer.astream = stream;
|
||||
|
||||
if (av_write_header(ctx) < 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
handle->muxer.ctx = ctx;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAX_FRAMES 64
|
||||
|
||||
static int SDLCALL ffemu_thread(void *data);
|
||||
|
||||
static bool init_thread(ffemu_t *handle)
|
||||
{
|
||||
handle->lock = SDL_CreateMutex();
|
||||
handle->cond_lock = SDL_CreateMutex();
|
||||
handle->cond = SDL_CreateCond();
|
||||
handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60);
|
||||
handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES);
|
||||
handle->video_fifo = fifo_new(512 * 448 * sizeof(int16_t) * MAX_FRAMES);
|
||||
|
||||
handle->alive = true;
|
||||
handle->can_sleep = true;
|
||||
handle->thread = SDL_CreateThread(ffemu_thread, handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void deinit_thread(ffemu_t *handle)
|
||||
{
|
||||
if (handle->thread)
|
||||
{
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
handle->alive = false;
|
||||
handle->can_sleep = false;
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
|
||||
SDL_CondSignal(handle->cond);
|
||||
SDL_WaitThread(handle->thread, NULL);
|
||||
handle->thread = NULL;
|
||||
|
||||
SDL_DestroyMutex(handle->lock);
|
||||
SDL_DestroyMutex(handle->cond_lock);
|
||||
SDL_DestroyCond(handle->cond);
|
||||
|
||||
fifo_free(handle->audio_fifo);
|
||||
fifo_free(handle->attr_fifo);
|
||||
fifo_free(handle->video_fifo);
|
||||
}
|
||||
}
|
||||
|
||||
ffemu_t *ffemu_new(const struct ffemu_params *params)
|
||||
|
@ -161,13 +216,16 @@ ffemu_t *ffemu_new(const struct ffemu_params *params)
|
|||
|
||||
handle->params = *params;
|
||||
|
||||
if (init_video(&handle->video, &handle->params) < 0)
|
||||
if (!init_video(&handle->video, &handle->params))
|
||||
goto error;
|
||||
|
||||
if (init_audio(&handle->audio, &handle->params) < 0)
|
||||
if (!init_audio(&handle->audio, &handle->params))
|
||||
goto error;
|
||||
|
||||
if (init_muxer(handle) < 0)
|
||||
if (!init_muxer(handle))
|
||||
goto error;
|
||||
|
||||
if (!init_thread(handle))
|
||||
goto error;
|
||||
|
||||
return handle;
|
||||
|
@ -181,6 +239,8 @@ void ffemu_free(ffemu_t *handle)
|
|||
{
|
||||
if (handle)
|
||||
{
|
||||
deinit_thread(handle);
|
||||
|
||||
if (handle->audio.codec)
|
||||
{
|
||||
avcodec_close(handle->audio.codec);
|
||||
|
@ -209,8 +269,78 @@ void ffemu_free(ffemu_t *handle)
|
|||
}
|
||||
}
|
||||
|
||||
// Need to make this thread based, but hey.
|
||||
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_mutexP(handle->lock);
|
||||
unsigned avail = fifo_write_avail(handle->attr_fifo);
|
||||
SDL_mutexV(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
|
||||
if (avail >= sizeof(*data))
|
||||
break;
|
||||
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
if (handle->can_sleep)
|
||||
{
|
||||
handle->can_sleep = false;
|
||||
SDL_CondWait(handle->cond, handle->cond_lock);
|
||||
handle->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(handle->cond);
|
||||
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
}
|
||||
|
||||
SDL_mutexP(handle->lock);
|
||||
fifo_write(handle->attr_fifo, data, sizeof(*data));
|
||||
fifo_write(handle->video_fifo, data->data, data->pitch * data->height);
|
||||
SDL_mutexV(handle->lock);
|
||||
SDL_CondSignal(handle->cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_mutexP(handle->lock);
|
||||
unsigned avail = fifo_write_avail(handle->audio_fifo);
|
||||
SDL_mutexV(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
|
||||
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
|
||||
break;
|
||||
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
if (handle->can_sleep)
|
||||
{
|
||||
handle->can_sleep = false;
|
||||
SDL_CondWait(handle->cond, handle->cond_lock);
|
||||
handle->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(handle->cond);
|
||||
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
}
|
||||
|
||||
SDL_mutexP(handle->lock);
|
||||
fifo_write(handle->audio_fifo, data->data, data->frames * handle->params.channels * sizeof(int16_t));
|
||||
SDL_mutexV(handle->lock);
|
||||
SDL_CondSignal(handle->cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
{
|
||||
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, PIX_FMT_RGB555LE,
|
||||
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
|
||||
|
@ -250,9 +380,8 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
|
||||
size_t written_frames = 0;
|
||||
while (written_frames < data->frames)
|
||||
{
|
||||
|
@ -279,13 +408,9 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
|||
|
||||
pkt.size = out_size;
|
||||
if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE)
|
||||
{
|
||||
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
}
|
||||
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
handle->audio.frames_in_buffer = 0;
|
||||
|
@ -303,6 +428,8 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
|||
|
||||
int ffemu_finalize(ffemu_t *handle)
|
||||
{
|
||||
deinit_thread(handle);
|
||||
|
||||
// Push out delayed frames. (MPEG codecs)
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
|
@ -335,3 +462,69 @@ int ffemu_finalize(ffemu_t *handle)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int SDLCALL ffemu_thread(void *data)
|
||||
{
|
||||
ffemu_t *ff = data;
|
||||
|
||||
uint16_t video_buf[512 * 448];
|
||||
int16_t audio_buf[128 * ff->params.channels];
|
||||
struct ffemu_video_data attr_buf;
|
||||
|
||||
while (ff->alive)
|
||||
{
|
||||
bool avail_video = false;
|
||||
bool avail_audio = false;
|
||||
|
||||
SDL_mutexP(ff->lock);
|
||||
if (fifo_read_avail(ff->attr_fifo) >= sizeof(attr_buf))
|
||||
avail_video = true;
|
||||
|
||||
if (fifo_read_avail(ff->audio_fifo) >= sizeof(audio_buf))
|
||||
avail_audio = true;
|
||||
SDL_mutexV(ff->lock);
|
||||
|
||||
if (!avail_video && !avail_audio)
|
||||
{
|
||||
SDL_mutexP(ff->cond_lock);
|
||||
if (ff->can_sleep)
|
||||
{
|
||||
ff->can_sleep = false;
|
||||
SDL_CondWait(ff->cond, ff->cond_lock);
|
||||
ff->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(ff->cond);
|
||||
|
||||
SDL_mutexV(ff->cond_lock);
|
||||
}
|
||||
|
||||
if (avail_video)
|
||||
{
|
||||
SDL_mutexP(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);
|
||||
SDL_mutexV(ff->lock);
|
||||
SDL_CondSignal(ff->cond);
|
||||
|
||||
attr_buf.data = video_buf;
|
||||
ffemu_push_video_thread(ff, &attr_buf);
|
||||
}
|
||||
|
||||
if (avail_audio)
|
||||
{
|
||||
SDL_mutexP(ff->lock);
|
||||
fifo_read(ff->audio_fifo, audio_buf, sizeof(audio_buf));
|
||||
SDL_mutexV(ff->lock);
|
||||
SDL_CondSignal(ff->cond);
|
||||
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = 128,
|
||||
.data = audio_buf
|
||||
};
|
||||
|
||||
ffemu_push_audio_thread(ff, &aud);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
33
ssnes.c
33
ssnes.c
|
@ -178,32 +178,35 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
|||
|
||||
static void audio_sample(uint16_t left, uint16_t right)
|
||||
{
|
||||
if ( !g_extern.audio_active )
|
||||
if (!g_extern.audio_active)
|
||||
return;
|
||||
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = left;
|
||||
#endif
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
|
||||
#ifdef HAVE_FFMPEG
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = right;
|
||||
#endif
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
|
||||
|
||||
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording)
|
||||
{
|
||||
static int16_t static_data[2];
|
||||
static_data[0] = left;
|
||||
static_data[1] = right;
|
||||
struct ffemu_audio_data ffemu_data = {
|
||||
.data = static_data,
|
||||
.frames = 1
|
||||
.data = g_extern.audio_data.conv_outsamples,
|
||||
.frames = g_extern.audio_data.data_ptr / 2
|
||||
};
|
||||
ffemu_push_audio(g_extern.rec, &ffemu_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
|
||||
|
||||
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
|
||||
return;
|
||||
|
||||
ssnes_dsp_input_t dsp_input = {
|
||||
.samples = g_extern.audio_data.data,
|
||||
.frames = g_extern.audio_data.data_ptr / 2
|
||||
|
|
Loading…
Reference in New Issue