Heyhey :D

This commit is contained in:
Themaister 2011-06-14 21:35:31 +02:00
parent f761811189
commit 54bc0a8d96
2 changed files with 236 additions and 40 deletions

View File

@ -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
View File

@ -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