Custom timing support for FFmpeg recording.

This commit is contained in:
Themaister 2011-11-16 18:56:42 +01:00
parent 88c870dcfa
commit 3e7c412738
6 changed files with 58 additions and 59 deletions

View File

@ -306,6 +306,11 @@ static bool environment_cb(unsigned cmd, void *data)
SSNES_LOG("GET_OVERSCAN: %u\n", (unsigned)!g_settings.video.crop_overscan);
break;
case SNES_ENVIRONMENT_SET_TIMING:
g_extern.system.timing = *(const struct snes_system_timing*)data;
g_extern.system.timing_set = true;
break;
default:
return false;
}

View File

@ -213,6 +213,8 @@ struct global
struct snes_geometry geom;
unsigned pitch; // If 0, has classic libsnes semantics.
char fullpath[MAXPATHLEN];
struct snes_system_timing timing;
bool timing_set;
} system;
struct

View File

@ -71,6 +71,8 @@ extern "C" {
#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * -- Window geometry information for the system/game.
#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * -- Pitch of game image.
#define SNES_ENVIRONMENT_GET_OVERSCAN 3 // bool * -- Boolean value whether or not the implementation should use overscan.
#define SNES_ENVIRONMENT_SET_TIMING 4 // const struct snes_system_timing * -- Set exact timings of the system.
// Used primarily for video recording.
struct snes_geometry
{
@ -80,6 +82,12 @@ struct snes_geometry
unsigned max_height; // Maximum possible height of system.
};
struct snes_system_timing
{
double fps;
double sample_rate;
};
typedef bool (*snes_environment_t)(unsigned cmd, void *data);
// Must be called before calling snes_init().

View File

@ -98,7 +98,7 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
#endif
audio->codec->sample_rate = param->samplerate;
audio->codec->time_base = (AVRational) { 1, param->samplerate };
audio->codec->time_base = av_d2q(1.0 / param->samplerate, 1000000);
audio->codec->channels = param->channels;
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
@ -162,7 +162,7 @@ static bool init_video(struct video_info *video, const struct ffemu_params *para
video->codec->width = param->out_width;
video->codec->height = param->out_height;
video->codec->time_base = (AVRational) { param->fps.den, param->fps.num };
video->codec->time_base = av_d2q(1.0 / param->fps, 1000000); // Arbitrary big number.
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
video->codec->pix_fmt = video->pix_fmt;
@ -399,7 +399,7 @@ void ffemu_free(ffemu_t *handle)
}
}
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
{
for (;;)
{
@ -408,7 +408,7 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
slock_unlock(handle->lock);
if (!handle->alive)
return -1;
return false;
if (avail >= sizeof(*data))
break;
@ -441,10 +441,10 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
slock_unlock(handle->lock);
scond_signal(handle->cond);
return 0;
return true;
}
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
{
for (;;)
{
@ -453,7 +453,7 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
slock_unlock(handle->lock);
if (!handle->alive)
return -1;
return false;
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
break;
@ -476,10 +476,10 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
slock_unlock(handle->lock);
scond_signal(handle->cond);
return 0;
return true;
}
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
static bool 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, handle->video.fmt,
handle->params.out_width, handle->params.out_height, handle->video.pix_fmt, SWS_POINT,
@ -496,7 +496,7 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
handle->video.outbuf_size, handle->video.conv_frame);
if (outsize < 0)
return -1;
return false;
AVPacket pkt;
av_init_packet(&pkt);
@ -513,15 +513,15 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
if (pkt.size > 0)
{
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
return -1;
return false;
}
handle->video.frame_cnt++;
return 0;
return true;
}
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
static bool ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
{
size_t written_frames = 0;
while (written_frames < data->frames)
@ -545,7 +545,7 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
int out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer);
if (out_size < 0)
return -1;
return false;
pkt.size = out_size;
@ -558,14 +558,14 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
if (pkt.size > 0)
{
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
return -1;
return false;
}
}
}
return 0;
return true;
}
int ffemu_finalize(ffemu_t *handle)
bool ffemu_finalize(ffemu_t *handle)
{
deinit_thread(handle);
@ -642,7 +642,7 @@ int ffemu_finalize(ffemu_t *handle)
// Write final data.
av_write_trailer(handle->muxer.ctx);
return 0;
return true;
}
static void ffemu_thread(void *data)

View File

@ -8,15 +8,14 @@
extern "C" {
#endif
struct ffemu_rational
{
unsigned num;
unsigned den;
};
// Parameters passed to ffemu_new()
struct ffemu_params
{
// FPS of input video.
double fps;
// Sample rate of input audio.
double samplerate;
// Desired output resolution.
unsigned out_width;
unsigned out_height;
@ -24,38 +23,17 @@ struct ffemu_params
// Total size of framebuffer used in input.
unsigned fb_width;
unsigned fb_height;
// Aspect ratio of input video. Parameters are passed to the muxer,
// the video itself is not scaled.
float aspect_ratio;
// FPS of video input.
struct ffemu_rational fps;
// Relative video quality. 0 is lossless (if available), 10 is very low quality.
// A value over 10 is codec defined if it will give even worse quality.
unsigned videoq;
// Define some video codec dependent option. (E.g. h264 profiles)
uint64_t video_opt;
// Audio sample rate.
unsigned samplerate;
// Audio channels.
unsigned channels;
// If input is ARGB or XRGB1555.
bool rgb32;
// Audio bits. Sample format is always signed PCM in native byte order.
//unsigned bits;
// Relative audio quality. 0 is lossless (if available), 10 is very low quality.
// A value over 10 is codec defined if it will give even worse quality.
// Some codecs might ignore this (lossless codecs such as FLAC).
unsigned audioq;
// Define some audio codec dependent option.
uint64_t audio_opt;
// Filename to dump to.
const char *filename;
};
@ -79,10 +57,9 @@ typedef struct ffemu ffemu_t;
ffemu_t *ffemu_new(const struct ffemu_params *params);
void ffemu_free(ffemu_t* handle);
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
int ffemu_finalize(ffemu_t *handle);
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
bool ffemu_finalize(ffemu_t *handle);
#ifdef __cplusplus
}

21
ssnes.c
View File

@ -958,20 +958,27 @@ static void init_recording(void)
if (!g_extern.recording)
return;
// Not perfectly accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
// Canonical values.
double fps = psnes_get_region() == SNES_REGION_NTSC ? 60.00 : 50.00;
double samplerate = 32000.0;
if (g_extern.system.timing_set)
{
fps = g_extern.system.timing.fps;
samplerate = g_extern.system.timing.sample_rate;
SSNES_LOG("Custom timing given: FPS: %.4lf, Sample rate: %.4lf\n", fps, samplerate);
}
struct ffemu_params params = {
.out_width = g_extern.system.geom.base_width,
.out_height = g_extern.system.geom.base_height,
.fb_width = g_extern.system.geom.max_width,
.fb_height = g_extern.system.geom.max_height,
.channels = 2,
.samplerate = 32000,
.filename = g_extern.record_path,
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
.fps = fps,
.samplerate = samplerate,
.rgb32 = false,
};