Custom timing support for FFmpeg recording.
This commit is contained in:
parent
88c870dcfa
commit
3e7c412738
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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().
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
21
ssnes.c
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue