mirror of
https://github.com/libretro/RetroArch
synced 2025-02-21 18:40:09 +00:00
Code reformatting (80-char limit); C-style comments
This commit is contained in:
parent
cb09818d71
commit
1d043121e2
15
autosave.c
15
autosave.c
@ -53,14 +53,15 @@ static void autosave_thread(void *data)
|
|||||||
|
|
||||||
if (differ)
|
if (differ)
|
||||||
{
|
{
|
||||||
// Should probably deal with this more elegantly.
|
/* Should probably deal with this more elegantly. */
|
||||||
FILE *file = fopen(save->path, "wb");
|
FILE *file = fopen(save->path, "wb");
|
||||||
if (file)
|
if (file)
|
||||||
{
|
{
|
||||||
// Avoid spamming down stderr ... :)
|
/* Avoid spamming down stderr ... */
|
||||||
if (first_log)
|
if (first_log)
|
||||||
{
|
{
|
||||||
RARCH_LOG("Autosaving SRAM to \"%s\", will continue to check every %u seconds ...\n", save->path, save->interval);
|
RARCH_LOG("Autosaving SRAM to \"%s\", will continue to check every %u seconds ...\n",
|
||||||
|
save->path, save->interval);
|
||||||
first_log = false;
|
first_log = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -76,13 +77,17 @@ static void autosave_thread(void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
slock_lock(save->cond_lock);
|
slock_lock(save->cond_lock);
|
||||||
|
|
||||||
if (!save->quit)
|
if (!save->quit)
|
||||||
scond_wait_timeout(save->cond, save->cond_lock, save->interval * 1000000LL);
|
scond_wait_timeout(save->cond, save->cond_lock,
|
||||||
|
save->interval * 1000000LL);
|
||||||
|
|
||||||
slock_unlock(save->cond_lock);
|
slock_unlock(save->cond_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval)
|
autosave_t *autosave_new(const char *path, const void *data, size_t size,
|
||||||
|
unsigned interval)
|
||||||
{
|
{
|
||||||
autosave_t *handle = (autosave_t*)calloc(1, sizeof(*handle));
|
autosave_t *handle = (autosave_t*)calloc(1, sizeof(*handle));
|
||||||
if (!handle)
|
if (!handle)
|
||||||
|
@ -39,7 +39,8 @@ const ffemu_backend_t *ffemu_find_backend(const char *ident)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ffemu_init_first(const ffemu_backend_t **backend, void **data, const struct ffemu_params *params)
|
bool ffemu_init_first(const ffemu_backend_t **backend, void **data,
|
||||||
|
const struct ffemu_params *params)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0; ffemu_backends[i]; i++)
|
for (i = 0; ffemu_backends[i]; i++)
|
||||||
|
@ -31,36 +31,37 @@ enum ffemu_pix_format
|
|||||||
FFEMU_PIX_ARGB8888
|
FFEMU_PIX_ARGB8888
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parameters passed to ffemu_new()
|
/* Parameters passed to ffemu_new() */
|
||||||
struct ffemu_params
|
struct ffemu_params
|
||||||
{
|
{
|
||||||
// FPS of input video.
|
/* FPS of input video. */
|
||||||
double fps;
|
double fps;
|
||||||
// Sample rate of input audio.
|
/* Sample rate of input audio. */
|
||||||
double samplerate;
|
double samplerate;
|
||||||
|
|
||||||
// Desired output resolution.
|
/* Desired output resolution. */
|
||||||
unsigned out_width;
|
unsigned out_width;
|
||||||
unsigned out_height;
|
unsigned out_height;
|
||||||
|
|
||||||
// Total size of framebuffer used in input.
|
/* Total size of framebuffer used in input. */
|
||||||
unsigned fb_width;
|
unsigned fb_width;
|
||||||
unsigned fb_height;
|
unsigned fb_height;
|
||||||
|
|
||||||
// Aspect ratio of input video. Parameters are passed to the muxer,
|
/* Aspect ratio of input video. Parameters are passed to the muxer,
|
||||||
// the video itself is not scaled.
|
* the video itself is not scaled.
|
||||||
|
*/
|
||||||
float aspect_ratio;
|
float aspect_ratio;
|
||||||
|
|
||||||
// Audio channels.
|
/* Audio channels. */
|
||||||
unsigned channels;
|
unsigned channels;
|
||||||
|
|
||||||
// Input pixel format.
|
/* Input pixel format. */
|
||||||
enum ffemu_pix_format pix_fmt;
|
enum ffemu_pix_format pix_fmt;
|
||||||
|
|
||||||
// Filename to dump to.
|
/* Filename to dump to. */
|
||||||
const char *filename;
|
const char *filename;
|
||||||
|
|
||||||
// Path to config. Optional.
|
/* Path to config. Optional. */
|
||||||
const char *config;
|
const char *config;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ typedef struct ffemu_backend
|
|||||||
{
|
{
|
||||||
void *(*init)(const struct ffemu_params *params);
|
void *(*init)(const struct ffemu_params *params);
|
||||||
void (*free)(void *data);
|
void (*free)(void *data);
|
||||||
bool (*push_video)(void *data, const struct ffemu_video_data *video_data);
|
bool (*push_video)(void *data,const struct ffemu_video_data *video_data);
|
||||||
bool (*push_audio)(void *data, const struct ffemu_audio_data *audio_data);
|
bool (*push_audio)(void *data, const struct ffemu_audio_data *audio_data);
|
||||||
bool (*finalize)(void *data);
|
bool (*finalize)(void *data);
|
||||||
const char *ident;
|
const char *ident;
|
||||||
@ -92,7 +93,8 @@ typedef struct ffemu_backend
|
|||||||
extern const ffemu_backend_t ffemu_ffmpeg;
|
extern const ffemu_backend_t ffemu_ffmpeg;
|
||||||
|
|
||||||
const ffemu_backend_t *ffemu_find_backend(const char *ident);
|
const ffemu_backend_t *ffemu_find_backend(const char *ident);
|
||||||
bool ffemu_init_first(const ffemu_backend_t **backend, void **data, const struct ffemu_params *params);
|
bool ffemu_init_first(const ffemu_backend_t **backend, void **data,
|
||||||
|
const struct ffemu_params *params);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
211
record/ffmpeg.c
211
record/ffmpeg.c
@ -105,9 +105,10 @@ struct ff_audio_info
|
|||||||
uint8_t *outbuf;
|
uint8_t *outbuf;
|
||||||
size_t outbuf_size;
|
size_t outbuf_size;
|
||||||
|
|
||||||
// Most lossy audio codecs only support certain sampling rates.
|
/* Most lossy audio codecs only support certain sampling rates.
|
||||||
// Could use libswresample, but it doesn't support floating point ratios. :(
|
* Could use libswresample, but it doesn't support floating point ratios.
|
||||||
// Use either S16 or (planar) float for simplicity.
|
* Use either S16 or (planar) float for simplicity.
|
||||||
|
*/
|
||||||
const rarch_resampler_t *resampler;
|
const rarch_resampler_t *resampler;
|
||||||
void *resampler_data;
|
void *resampler_data;
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ struct ff_config_param
|
|||||||
unsigned scale_factor;
|
unsigned scale_factor;
|
||||||
|
|
||||||
bool audio_enable;
|
bool audio_enable;
|
||||||
// Keep same naming conventions as libavcodec.
|
/* Keep same naming conventions as libavcodec. */
|
||||||
bool audio_qscale;
|
bool audio_qscale;
|
||||||
int audio_global_quality;
|
int audio_global_quality;
|
||||||
int audio_bit_rate;
|
int audio_bit_rate;
|
||||||
@ -183,7 +184,8 @@ typedef struct ffmpeg
|
|||||||
volatile bool can_sleep;
|
volatile bool can_sleep;
|
||||||
} ffmpeg_t;
|
} ffmpeg_t;
|
||||||
|
|
||||||
static bool ffmpeg_codec_has_sample_format(enum AVSampleFormat fmt, const enum AVSampleFormat *fmts)
|
static bool ffmpeg_codec_has_sample_format(enum AVSampleFormat fmt,
|
||||||
|
const enum AVSampleFormat *fmts)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++)
|
for (i = 0; fmts[i] != AV_SAMPLE_FMT_NONE; i++)
|
||||||
@ -192,7 +194,8 @@ static bool ffmpeg_codec_has_sample_format(enum AVSampleFormat fmt, const enum A
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffmpeg_audio_resolve_format(struct ff_audio_info *audio, const AVCodec *codec)
|
static void ffmpeg_audio_resolve_format(struct ff_audio_info *audio,
|
||||||
|
const AVCodec *codec)
|
||||||
{
|
{
|
||||||
audio->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
|
audio->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
|
||||||
|
|
||||||
@ -227,18 +230,19 @@ static void ffmpeg_audio_resolve_format(struct ff_audio_info *audio, const AVCod
|
|||||||
audio->sample_size = audio->use_float ? sizeof(float) : sizeof(int16_t);
|
audio->sample_size = audio->use_float ? sizeof(float) : sizeof(int16_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t *handle, const AVCodec *codec)
|
static void ffmpeg_audio_resolve_sample_rate(ffmpeg_t *handle,
|
||||||
|
const AVCodec *codec)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
struct ff_config_param *params = &handle->config;
|
struct ff_config_param *params = &handle->config;
|
||||||
struct ffemu_params *param = &handle->params;
|
struct ffemu_params *param = &handle->params;
|
||||||
|
|
||||||
// We'll have to force resampling to some supported sampling rate.
|
/* We'll have to force resampling to some supported sampling rate. */
|
||||||
if (codec->supported_samplerates && !params->sample_rate)
|
if (codec->supported_samplerates && !params->sample_rate)
|
||||||
{
|
{
|
||||||
int input_rate = (int)param->samplerate;
|
int input_rate = (int)param->samplerate;
|
||||||
|
|
||||||
// Favor closest sampling rate, but always prefer ratio > 1.0.
|
/* Favor closest sampling rate, but always prefer ratio > 1.0. */
|
||||||
int best_rate = codec->supported_samplerates[0];
|
int best_rate = codec->supported_samplerates[0];
|
||||||
int best_diff = best_rate - input_rate;
|
int best_diff = best_rate - input_rate;
|
||||||
|
|
||||||
@ -271,10 +275,12 @@ static bool ffmpeg_init_audio(ffmpeg_t *handle)
|
|||||||
struct ff_video_info *video = &handle->video;
|
struct ff_video_info *video = &handle->video;
|
||||||
struct ffemu_params *param = &handle->params;
|
struct ffemu_params *param = &handle->params;
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_encoder_by_name(*params->acodec ? params->acodec : "flac");
|
AVCodec *codec = avcodec_find_encoder_by_name(
|
||||||
|
*params->acodec ? params->acodec : "flac");
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
RARCH_ERR("[FFmpeg]: Cannot find acodec %s.\n", *params->acodec ? params->acodec : "flac");
|
RARCH_ERR("[FFmpeg]: Cannot find acodec %s.\n",
|
||||||
|
*params->acodec ? params->acodec : "flac");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +290,8 @@ static bool ffmpeg_init_audio(ffmpeg_t *handle)
|
|||||||
|
|
||||||
audio->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
audio->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
audio->codec->channels = param->channels;
|
audio->codec->channels = param->channels;
|
||||||
audio->codec->channel_layout = param->channels > 1 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
|
audio->codec->channel_layout = param->channels > 1
|
||||||
|
? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
|
||||||
|
|
||||||
ffmpeg_audio_resolve_format(audio, codec);
|
ffmpeg_audio_resolve_format(audio, codec);
|
||||||
ffmpeg_audio_resolve_sample_rate(handle, codec);
|
ffmpeg_audio_resolve_sample_rate(handle, codec);
|
||||||
@ -315,7 +322,7 @@ static bool ffmpeg_init_audio(ffmpeg_t *handle)
|
|||||||
else if (params->audio_bit_rate)
|
else if (params->audio_bit_rate)
|
||||||
audio->codec->bit_rate = params->audio_bit_rate;
|
audio->codec->bit_rate = params->audio_bit_rate;
|
||||||
|
|
||||||
// Allow experimental codecs.
|
/* Allow experimental codecs. */
|
||||||
audio->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
|
audio->codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
|
||||||
|
|
||||||
if (handle->muxer.ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
if (handle->muxer.ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||||
@ -324,7 +331,7 @@ static bool ffmpeg_init_audio(ffmpeg_t *handle)
|
|||||||
if (avcodec_open2(audio->codec, codec, params->audio_opts ? ¶ms->audio_opts : NULL) != 0)
|
if (avcodec_open2(audio->codec, codec, params->audio_opts ? ¶ms->audio_opts : NULL) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!audio->codec->frame_size) // If not set (PCM), just set something.
|
if (!audio->codec->frame_size) /* If not set (PCM), just set something. */
|
||||||
audio->codec->frame_size = 1024;
|
audio->codec->frame_size = 1024;
|
||||||
|
|
||||||
audio->buffer = (uint8_t*)av_malloc(
|
audio->buffer = (uint8_t*)av_malloc(
|
||||||
@ -332,7 +339,9 @@ static bool ffmpeg_init_audio(ffmpeg_t *handle)
|
|||||||
audio->codec->channels *
|
audio->codec->channels *
|
||||||
audio->sample_size);
|
audio->sample_size);
|
||||||
|
|
||||||
//RARCH_LOG("[FFmpeg]: Audio frame size: %d.\n", audio->codec->frame_size);
|
#if 0
|
||||||
|
RARCH_LOG("[FFmpeg]: Audio frame size: %d.\n", audio->codec->frame_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!audio->buffer)
|
if (!audio->buffer)
|
||||||
return false;
|
return false;
|
||||||
@ -357,23 +366,25 @@ static bool ffmpeg_init_video(ffmpeg_t *handle)
|
|||||||
codec = avcodec_find_encoder_by_name(params->vcodec);
|
codec = avcodec_find_encoder_by_name(params->vcodec);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// By default, lossless video.
|
/* By default, lossless video. */
|
||||||
av_dict_set(¶ms->video_opts, "qp", "0", 0);
|
av_dict_set(¶ms->video_opts, "qp", "0", 0);
|
||||||
codec = avcodec_find_encoder_by_name("libx264rgb");
|
codec = avcodec_find_encoder_by_name("libx264rgb");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!codec)
|
if (!codec)
|
||||||
{
|
{
|
||||||
RARCH_ERR("[FFmpeg]: Cannot find vcodec %s.\n", *params->vcodec ? params->vcodec : "libx264rgb");
|
RARCH_ERR("[FFmpeg]: Cannot find vcodec %s.\n",
|
||||||
|
*params->vcodec ? params->vcodec : "libx264rgb");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
video->encoder = codec;
|
video->encoder = codec;
|
||||||
|
|
||||||
// Don't use swscaler unless format is not something "in-house" scaler supports.
|
/* Don't use swscaler unless format is not something "in-house" scaler supports.
|
||||||
// libswscale doesn't scale RGB -> RGB correctly (goes via YUV first), and it's non-trivial to fix
|
* libswscale doesn't scale RGB -> RGB correctly (goes via YUV first), and it's non-trivial to fix
|
||||||
// upstream as it's heavily geared towards YUV.
|
* upstream as it's heavily geared towards YUV.
|
||||||
// If we're dealing with strange formats or YUV, just use libswscale.
|
* If we're dealing with strange formats or YUV, just use libswscale.
|
||||||
|
*/
|
||||||
if (params->out_pix_fmt != PIX_FMT_NONE)
|
if (params->out_pix_fmt != PIX_FMT_NONE)
|
||||||
{
|
{
|
||||||
video->pix_fmt = params->out_pix_fmt;
|
video->pix_fmt = params->out_pix_fmt;
|
||||||
@ -394,7 +405,7 @@ static bool ffmpeg_init_video(ffmpeg_t *handle)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Use BGR24 as default out format.
|
else /* Use BGR24 as default out format. */
|
||||||
{
|
{
|
||||||
video->pix_fmt = PIX_FMT_BGR24;
|
video->pix_fmt = PIX_FMT_BGR24;
|
||||||
video->scaler.out_fmt = SCALER_FMT_BGR24;
|
video->scaler.out_fmt = SCALER_FMT_BGR24;
|
||||||
@ -426,15 +437,16 @@ static bool ffmpeg_init_video(ffmpeg_t *handle)
|
|||||||
|
|
||||||
video->codec = avcodec_alloc_context3(codec);
|
video->codec = avcodec_alloc_context3(codec);
|
||||||
|
|
||||||
// Useful to set scale_factor to 2 for chroma subsampled formats to maintain full chroma resolution.
|
/* Useful to set scale_factor to 2 for chroma subsampled formats to
|
||||||
// (Or just use 4:4:4 or RGB ...)
|
* maintain full chroma resolution. (Or just use 4:4:4 or RGB ...)
|
||||||
|
*/
|
||||||
param->out_width *= params->scale_factor;
|
param->out_width *= params->scale_factor;
|
||||||
param->out_height *= params->scale_factor;
|
param->out_height *= params->scale_factor;
|
||||||
|
|
||||||
video->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
video->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
video->codec->width = param->out_width;
|
video->codec->width = param->out_width;
|
||||||
video->codec->height = param->out_height;
|
video->codec->height = param->out_height;
|
||||||
video->codec->time_base = av_d2q((double)params->frame_drop_ratio / param->fps, 1000000); // Arbitrary big number.
|
video->codec->time_base = av_d2q((double)params->frame_drop_ratio /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->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
|
||||||
video->codec->pix_fmt = video->pix_fmt;
|
video->codec->pix_fmt = video->pix_fmt;
|
||||||
|
|
||||||
@ -451,25 +463,30 @@ static bool ffmpeg_init_video(ffmpeg_t *handle)
|
|||||||
if (handle->muxer.ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
if (handle->muxer.ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||||
video->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
video->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
if (avcodec_open2(video->codec, codec, params->video_opts ? ¶ms->video_opts : NULL) != 0)
|
if (avcodec_open2(video->codec, codec, params->video_opts ?
|
||||||
|
¶ms->video_opts : NULL) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
|
/* Allocate a big buffer. ffmpeg API doesn't seem to give us some
|
||||||
|
* clues how big this buffer should be.
|
||||||
|
*/
|
||||||
video->outbuf_size = 1 << 23;
|
video->outbuf_size = 1 << 23;
|
||||||
video->outbuf = (uint8_t*)av_malloc(video->outbuf_size);
|
video->outbuf = (uint8_t*)av_malloc(video->outbuf_size);
|
||||||
|
|
||||||
video->frame_drop_ratio = params->frame_drop_ratio;
|
video->frame_drop_ratio = params->frame_drop_ratio;
|
||||||
|
|
||||||
size_t size = avpicture_get_size(video->pix_fmt, param->out_width, param->out_height);
|
size_t size = avpicture_get_size(video->pix_fmt, param->out_width,
|
||||||
|
param->out_height);
|
||||||
video->conv_frame_buf = (uint8_t*)av_malloc(size);
|
video->conv_frame_buf = (uint8_t*)av_malloc(size);
|
||||||
video->conv_frame = av_frame_alloc();
|
video->conv_frame = av_frame_alloc();
|
||||||
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, video->pix_fmt,
|
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf,
|
||||||
param->out_width, param->out_height);
|
video->pix_fmt, param->out_width, param->out_height);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ffmpeg_init_config(struct ff_config_param *params, const char *config)
|
static bool ffmpeg_init_config(struct ff_config_param *params,
|
||||||
|
const char *config)
|
||||||
{
|
{
|
||||||
params->out_pix_fmt = PIX_FMT_NONE;
|
params->out_pix_fmt = PIX_FMT_NONE;
|
||||||
params->scale_factor = 1;
|
params->scale_factor = 1;
|
||||||
@ -486,14 +503,17 @@ static bool ffmpeg_init_config(struct ff_config_param *params, const char *confi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
config_get_array(params->conf, "vcodec", params->vcodec, sizeof(params->vcodec));
|
config_get_array(params->conf, "vcodec", params->vcodec,
|
||||||
config_get_array(params->conf, "acodec", params->acodec, sizeof(params->acodec));
|
sizeof(params->vcodec));
|
||||||
config_get_array(params->conf, "format", params->format, sizeof(params->format));
|
config_get_array(params->conf, "acodec", params->acodec,
|
||||||
|
sizeof(params->acodec));
|
||||||
|
config_get_array(params->conf, "format", params->format,
|
||||||
|
sizeof(params->format));
|
||||||
|
|
||||||
config_get_uint(params->conf, "threads", ¶ms->threads);
|
config_get_uint(params->conf, "threads", ¶ms->threads);
|
||||||
|
|
||||||
if (!config_get_uint(params->conf, "frame_drop_ratio", ¶ms->frame_drop_ratio)
|
if (!config_get_uint(params->conf, "frame_drop_ratio",
|
||||||
|| !params->frame_drop_ratio)
|
¶ms->frame_drop_ratio) || !params->frame_drop_ratio)
|
||||||
params->frame_drop_ratio = 1;
|
params->frame_drop_ratio = 1;
|
||||||
|
|
||||||
if (!config_get_bool(params->conf, "audio_enable", ¶ms->audio_enable))
|
if (!config_get_bool(params->conf, "audio_enable", ¶ms->audio_enable))
|
||||||
@ -502,9 +522,11 @@ static bool ffmpeg_init_config(struct ff_config_param *params, const char *confi
|
|||||||
config_get_uint(params->conf, "sample_rate", ¶ms->sample_rate);
|
config_get_uint(params->conf, "sample_rate", ¶ms->sample_rate);
|
||||||
config_get_uint(params->conf, "scale_factor", ¶ms->scale_factor);
|
config_get_uint(params->conf, "scale_factor", ¶ms->scale_factor);
|
||||||
|
|
||||||
params->audio_qscale = config_get_int(params->conf, "audio_global_quality", ¶ms->audio_global_quality);
|
params->audio_qscale = config_get_int(params->conf, "audio_global_quality",
|
||||||
|
¶ms->audio_global_quality);
|
||||||
config_get_int(params->conf, "audio_bit_rate", ¶ms->audio_bit_rate);
|
config_get_int(params->conf, "audio_bit_rate", ¶ms->audio_bit_rate);
|
||||||
params->video_qscale = config_get_int(params->conf, "video_global_quality", ¶ms->video_global_quality);
|
params->video_qscale = config_get_int(params->conf, "video_global_quality",
|
||||||
|
¶ms->video_global_quality);
|
||||||
config_get_int(params->conf, "video_bit_rate", ¶ms->video_bit_rate);
|
config_get_int(params->conf, "video_bit_rate", ¶ms->video_bit_rate);
|
||||||
|
|
||||||
char pix_fmt[64] = {0};
|
char pix_fmt[64] = {0};
|
||||||
@ -545,7 +567,7 @@ static bool ffmpeg_init_muxer_pre(ffmpeg_t *handle)
|
|||||||
av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename));
|
av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename));
|
||||||
|
|
||||||
if (*handle->config.format)
|
if (*handle->config.format)
|
||||||
ctx->oformat = av_guess_format(handle->config.format, NULL, NULL);
|
ctx->oformat = av_guess_format(handle->config.format, NULL, NULL);
|
||||||
else
|
else
|
||||||
ctx->oformat = av_guess_format(NULL, ctx->filename, NULL);
|
ctx->oformat = av_guess_format(NULL, ctx->filename, NULL);
|
||||||
|
|
||||||
@ -564,19 +586,23 @@ static bool ffmpeg_init_muxer_pre(ffmpeg_t *handle)
|
|||||||
|
|
||||||
static bool ffmpeg_init_muxer_post(ffmpeg_t *handle)
|
static bool ffmpeg_init_muxer_post(ffmpeg_t *handle)
|
||||||
{
|
{
|
||||||
AVStream *stream = avformat_new_stream(handle->muxer.ctx, handle->video.encoder);
|
AVStream *stream = avformat_new_stream(handle->muxer.ctx,
|
||||||
|
handle->video.encoder);
|
||||||
stream->codec = handle->video.codec;
|
stream->codec = handle->video.codec;
|
||||||
handle->muxer.vstream = stream;
|
handle->muxer.vstream = stream;
|
||||||
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
|
handle->muxer.vstream->sample_aspect_ratio =
|
||||||
|
handle->video.codec->sample_aspect_ratio;
|
||||||
|
|
||||||
if (handle->config.audio_enable)
|
if (handle->config.audio_enable)
|
||||||
{
|
{
|
||||||
stream = avformat_new_stream(handle->muxer.ctx, handle->audio.encoder);
|
stream = avformat_new_stream(handle->muxer.ctx,
|
||||||
|
handle->audio.encoder);
|
||||||
stream->codec = handle->audio.codec;
|
stream->codec = handle->audio.codec;
|
||||||
handle->muxer.astream = stream;
|
handle->muxer.astream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
av_dict_set(&handle->muxer.ctx->metadata, "title", "RetroArch video dump", 0);
|
av_dict_set(&handle->muxer.ctx->metadata, "title",
|
||||||
|
"RetroArch video dump", 0);
|
||||||
|
|
||||||
return avformat_write_header(handle->muxer.ctx, NULL) >= 0;
|
return avformat_write_header(handle->muxer.ctx, NULL) >= 0;
|
||||||
}
|
}
|
||||||
@ -590,7 +616,8 @@ static bool init_thread(ffmpeg_t *handle)
|
|||||||
handle->lock = slock_new();
|
handle->lock = slock_new();
|
||||||
handle->cond_lock = slock_new();
|
handle->cond_lock = slock_new();
|
||||||
handle->cond = scond_new();
|
handle->cond = scond_new();
|
||||||
handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60); // Some arbitrary max size.
|
handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) *
|
||||||
|
handle->params.channels * MAX_FRAMES / 60); /* Some arbitrary max size. */
|
||||||
handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES);
|
handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES);
|
||||||
handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height *
|
handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height *
|
||||||
handle->video.pix_size * MAX_FRAMES);
|
handle->video.pix_size * MAX_FRAMES);
|
||||||
@ -732,7 +759,8 @@ error:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ffmpeg_push_video(void *data, const struct ffemu_video_data *video_data)
|
static bool ffmpeg_push_video(void *data,
|
||||||
|
const struct ffemu_video_data *video_data)
|
||||||
{
|
{
|
||||||
unsigned y;
|
unsigned y;
|
||||||
bool drop_frame;
|
bool drop_frame;
|
||||||
@ -775,7 +803,9 @@ static bool ffmpeg_push_video(void *data, const struct ffemu_video_data *video_d
|
|||||||
|
|
||||||
slock_lock(handle->lock);
|
slock_lock(handle->lock);
|
||||||
|
|
||||||
// Tightly pack our frame to conserve memory. libretro tends to use a very large pitch.
|
/* Tightly pack our frame to conserve memory.
|
||||||
|
* libretro tends to use a very large pitch.
|
||||||
|
*/
|
||||||
struct ffemu_video_data attr_data = *video_data;
|
struct ffemu_video_data attr_data = *video_data;
|
||||||
|
|
||||||
if (attr_data.is_dupe)
|
if (attr_data.is_dupe)
|
||||||
@ -787,7 +817,8 @@ static bool ffmpeg_push_video(void *data, const struct ffemu_video_data *video_d
|
|||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (y = 0; y < attr_data.height; y++, offset += video_data->pitch)
|
for (y = 0; y < attr_data.height; y++, offset += video_data->pitch)
|
||||||
fifo_write(handle->video_fifo, (const uint8_t*)video_data->data + offset, attr_data.pitch);
|
fifo_write(handle->video_fifo,
|
||||||
|
(const uint8_t*)video_data->data + offset, attr_data.pitch);
|
||||||
|
|
||||||
slock_unlock(handle->lock);
|
slock_unlock(handle->lock);
|
||||||
scond_signal(handle->cond);
|
scond_signal(handle->cond);
|
||||||
@ -795,7 +826,8 @@ static bool ffmpeg_push_video(void *data, const struct ffemu_video_data *video_d
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ffmpeg_push_audio(void *data, const struct ffemu_audio_data *audio_data)
|
static bool ffmpeg_push_audio(void *data,
|
||||||
|
const struct ffemu_audio_data *audio_data)
|
||||||
{
|
{
|
||||||
ffmpeg_t *handle = (ffmpeg_t*)data;
|
ffmpeg_t *handle = (ffmpeg_t*)data;
|
||||||
|
|
||||||
@ -814,7 +846,8 @@ static bool ffmpeg_push_audio(void *data, const struct ffemu_audio_data *audio_d
|
|||||||
if (!handle->alive)
|
if (!handle->alive)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (avail >= audio_data->frames * handle->params.channels * sizeof(int16_t))
|
if (avail >= audio_data->frames * handle->params.channels
|
||||||
|
* sizeof(int16_t))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
slock_lock(handle->cond_lock);
|
slock_lock(handle->cond_lock);
|
||||||
@ -831,7 +864,8 @@ static bool ffmpeg_push_audio(void *data, const struct ffemu_audio_data *audio_d
|
|||||||
}
|
}
|
||||||
|
|
||||||
slock_lock(handle->lock);
|
slock_lock(handle->lock);
|
||||||
fifo_write(handle->audio_fifo, audio_data->data, audio_data->frames * handle->params.channels * sizeof(int16_t));
|
fifo_write(handle->audio_fifo, audio_data->data,
|
||||||
|
audio_data->frames * handle->params.channels * sizeof(int16_t));
|
||||||
slock_unlock(handle->lock);
|
slock_unlock(handle->lock);
|
||||||
scond_signal(handle->cond);
|
scond_signal(handle->cond);
|
||||||
|
|
||||||
@ -872,30 +906,37 @@ static bool encode_video(ffmpeg_t *handle, AVPacket *pkt, AVFrame *frame)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffmpeg_scale_input(ffmpeg_t *handle, const struct ffemu_video_data *data)
|
static void ffmpeg_scale_input(ffmpeg_t *handle,
|
||||||
|
const struct ffemu_video_data *data)
|
||||||
{
|
{
|
||||||
// Attempt to preserve more information if we scale down.
|
/* Attempt to preserve more information if we scale down. */
|
||||||
bool shrunk = handle->params.out_width < data->width || handle->params.out_height < data->height;
|
bool shrunk = handle->params.out_width < data->width
|
||||||
|
|| handle->params.out_height < data->height;
|
||||||
|
|
||||||
if (handle->video.use_sws)
|
if (handle->video.use_sws)
|
||||||
{
|
{
|
||||||
handle->video.sws = sws_getCachedContext(handle->video.sws, data->width, data->height, handle->video.in_pix_fmt,
|
handle->video.sws = sws_getCachedContext(handle->video.sws,
|
||||||
handle->params.out_width, handle->params.out_height, handle->video.pix_fmt,
|
data->width, data->height, handle->video.in_pix_fmt,
|
||||||
|
handle->params.out_width, handle->params.out_height,
|
||||||
|
handle->video.pix_fmt,
|
||||||
shrunk ? SWS_BILINEAR : SWS_POINT, NULL, NULL, NULL);
|
shrunk ? SWS_BILINEAR : SWS_POINT, NULL, NULL, NULL);
|
||||||
|
|
||||||
int linesize = data->pitch;
|
int linesize = data->pitch;
|
||||||
sws_scale(handle->video.sws, (const uint8_t* const*)&data->data, &linesize, 0,
|
sws_scale(handle->video.sws, (const uint8_t* const*)&data->data,
|
||||||
data->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
|
&linesize, 0, data->height, handle->video.conv_frame->data,
|
||||||
|
handle->video.conv_frame->linesize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((int)data->width != handle->video.scaler.in_width || (int)data->height != handle->video.scaler.in_height)
|
if ((int)data->width != handle->video.scaler.in_width
|
||||||
|
|| (int)data->height != handle->video.scaler.in_height)
|
||||||
{
|
{
|
||||||
handle->video.scaler.in_width = data->width;
|
handle->video.scaler.in_width = data->width;
|
||||||
handle->video.scaler.in_height = data->height;
|
handle->video.scaler.in_height = data->height;
|
||||||
handle->video.scaler.in_stride = data->pitch;
|
handle->video.scaler.in_stride = data->pitch;
|
||||||
|
|
||||||
handle->video.scaler.scaler_type = shrunk ? SCALER_TYPE_BILINEAR : SCALER_TYPE_POINT;
|
handle->video.scaler.scaler_type = shrunk ?
|
||||||
|
SCALER_TYPE_BILINEAR : SCALER_TYPE_POINT;
|
||||||
|
|
||||||
handle->video.scaler.out_width = handle->params.out_width;
|
handle->video.scaler.out_width = handle->params.out_width;
|
||||||
handle->video.scaler.out_height = handle->params.out_height;
|
handle->video.scaler.out_height = handle->params.out_height;
|
||||||
@ -904,11 +945,13 @@ static void ffmpeg_scale_input(ffmpeg_t *handle, const struct ffemu_video_data *
|
|||||||
scaler_ctx_gen_filter(&handle->video.scaler);
|
scaler_ctx_gen_filter(&handle->video.scaler);
|
||||||
}
|
}
|
||||||
|
|
||||||
scaler_ctx_scale(&handle->video.scaler, handle->video.conv_frame->data[0], data->data);
|
scaler_ctx_scale(&handle->video.scaler,
|
||||||
|
handle->video.conv_frame->data[0], data->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ffmpeg_push_video_thread(ffmpeg_t *handle, const struct ffemu_video_data *data)
|
static bool ffmpeg_push_video_thread(ffmpeg_t *handle,
|
||||||
|
const struct ffemu_video_data *data)
|
||||||
{
|
{
|
||||||
if (!data->is_dupe)
|
if (!data->is_dupe)
|
||||||
ffmpeg_scale_input(handle, data);
|
ffmpeg_scale_input(handle, data);
|
||||||
@ -957,7 +1000,8 @@ static void planarize_audio(ffmpeg_t *handle)
|
|||||||
if (handle->audio.frames_in_buffer > handle->audio.planar_buf_frames)
|
if (handle->audio.frames_in_buffer > handle->audio.planar_buf_frames)
|
||||||
{
|
{
|
||||||
handle->audio.planar_buf = av_realloc(handle->audio.planar_buf,
|
handle->audio.planar_buf = av_realloc(handle->audio.planar_buf,
|
||||||
handle->audio.frames_in_buffer * handle->params.channels * handle->audio.sample_size);
|
handle->audio.frames_in_buffer * handle->params.channels *
|
||||||
|
handle->audio.sample_size);
|
||||||
if (!handle->audio.planar_buf)
|
if (!handle->audio.planar_buf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -989,13 +1033,15 @@ static bool encode_audio(ffmpeg_t *handle, AVPacket *pkt, bool dry)
|
|||||||
|
|
||||||
planarize_audio(handle);
|
planarize_audio(handle);
|
||||||
|
|
||||||
int samples_size = av_samples_get_buffer_size(NULL, handle->audio.codec->channels,
|
int samples_size = av_samples_get_buffer_size(NULL,
|
||||||
|
handle->audio.codec->channels,
|
||||||
handle->audio.frames_in_buffer,
|
handle->audio.frames_in_buffer,
|
||||||
handle->audio.codec->sample_fmt, 0);
|
handle->audio.codec->sample_fmt, 0);
|
||||||
|
|
||||||
avcodec_fill_audio_frame(frame, handle->audio.codec->channels,
|
avcodec_fill_audio_frame(frame, handle->audio.codec->channels,
|
||||||
handle->audio.codec->sample_fmt,
|
handle->audio.codec->sample_fmt,
|
||||||
handle->audio.is_planar ? (uint8_t*)handle->audio.planar_buf : handle->audio.buffer,
|
handle->audio.is_planar ? (uint8_t*)handle->audio.planar_buf :
|
||||||
|
handle->audio.buffer,
|
||||||
samples_size, 0);
|
samples_size, 0);
|
||||||
|
|
||||||
int got_packet = 0;
|
int got_packet = 0;
|
||||||
@ -1035,7 +1081,8 @@ static bool encode_audio(ffmpeg_t *handle, AVPacket *pkt, bool dry)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffmpeg_audio_resample(ffmpeg_t *handle, struct ffemu_audio_data *data)
|
static void ffmpeg_audio_resample(ffmpeg_t *handle,
|
||||||
|
struct ffemu_audio_data *data)
|
||||||
{
|
{
|
||||||
if (!handle->audio.use_float && !handle->audio.resampler)
|
if (!handle->audio.use_float && !handle->audio.resampler)
|
||||||
return;
|
return;
|
||||||
@ -1049,15 +1096,17 @@ static void ffmpeg_audio_resample(ffmpeg_t *handle, struct ffemu_audio_data *dat
|
|||||||
|
|
||||||
handle->audio.float_conv_frames = data->frames;
|
handle->audio.float_conv_frames = data->frames;
|
||||||
|
|
||||||
// To make sure we don't accidentially overflow.
|
/* To make sure we don't accidentially overflow. */
|
||||||
handle->audio.resample_out_frames = data->frames * handle->audio.ratio + 16;
|
handle->audio.resample_out_frames = data->frames * handle->audio.ratio + 16;
|
||||||
|
|
||||||
handle->audio.resample_out = (float*)av_realloc(handle->audio.resample_out,
|
handle->audio.resample_out = (float*)av_realloc(handle->audio.resample_out,
|
||||||
handle->audio.resample_out_frames * handle->params.channels * sizeof(float));
|
handle->audio.resample_out_frames *
|
||||||
|
handle->params.channels * sizeof(float));
|
||||||
if (!handle->audio.resample_out)
|
if (!handle->audio.resample_out)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handle->audio.fixed_conv_frames = max(handle->audio.resample_out_frames, handle->audio.float_conv_frames);
|
handle->audio.fixed_conv_frames = max(handle->audio.resample_out_frames,
|
||||||
|
handle->audio.float_conv_frames);
|
||||||
handle->audio.fixed_conv = (int16_t*)av_realloc(handle->audio.fixed_conv,
|
handle->audio.fixed_conv = (int16_t*)av_realloc(handle->audio.fixed_conv,
|
||||||
handle->audio.fixed_conv_frames * handle->params.channels * sizeof(int16_t));
|
handle->audio.fixed_conv_frames * handle->params.channels * sizeof(int16_t));
|
||||||
if (!handle->audio.fixed_conv)
|
if (!handle->audio.fixed_conv)
|
||||||
@ -1073,27 +1122,30 @@ static void ffmpeg_audio_resample(ffmpeg_t *handle, struct ffemu_audio_data *dat
|
|||||||
|
|
||||||
if (handle->audio.resampler)
|
if (handle->audio.resampler)
|
||||||
{
|
{
|
||||||
// It's always two channels ...
|
/* It's always two channels ... */
|
||||||
struct resampler_data info = {0};
|
struct resampler_data info = {0};
|
||||||
info.data_in = (const float*)data->data;
|
info.data_in = (const float*)data->data;
|
||||||
info.data_out = handle->audio.resample_out;
|
info.data_out = handle->audio.resample_out;
|
||||||
info.input_frames = data->frames;
|
info.input_frames = data->frames;
|
||||||
info.ratio = handle->audio.ratio;
|
info.ratio = handle->audio.ratio;
|
||||||
|
|
||||||
rarch_resampler_process(handle->audio.resampler, handle->audio.resampler_data, &info);
|
rarch_resampler_process(handle->audio.resampler,
|
||||||
|
handle->audio.resampler_data, &info);
|
||||||
data->data = handle->audio.resample_out;
|
data->data = handle->audio.resample_out;
|
||||||
data->frames = info.output_frames;
|
data->frames = info.output_frames;
|
||||||
|
|
||||||
if (!handle->audio.use_float)
|
if (!handle->audio.use_float)
|
||||||
{
|
{
|
||||||
audio_convert_float_to_s16(handle->audio.fixed_conv, handle->audio.resample_out,
|
audio_convert_float_to_s16(handle->audio.fixed_conv,
|
||||||
|
handle->audio.resample_out,
|
||||||
data->frames * handle->params.channels);
|
data->frames * handle->params.channels);
|
||||||
data->data = handle->audio.fixed_conv;
|
data->data = handle->audio.fixed_conv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ffmpeg_push_audio_thread(ffmpeg_t *handle, struct ffemu_audio_data *data, bool require_block)
|
static bool ffmpeg_push_audio_thread(ffmpeg_t *handle,
|
||||||
|
struct ffemu_audio_data *data, bool require_block)
|
||||||
{
|
{
|
||||||
ffmpeg_audio_resample(handle, data);
|
ffmpeg_audio_resample(handle, data);
|
||||||
|
|
||||||
@ -1135,7 +1187,8 @@ static bool ffmpeg_push_audio_thread(ffmpeg_t *handle, struct ffemu_audio_data *
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ffmpeg_flush_audio(ffmpeg_t *handle, void *audio_buf, size_t audio_buf_size)
|
static void ffmpeg_flush_audio(ffmpeg_t *handle, void *audio_buf,
|
||||||
|
size_t audio_buf_size)
|
||||||
{
|
{
|
||||||
size_t avail = fifo_read_avail(handle->audio_fifo);
|
size_t avail = fifo_read_avail(handle->audio_fifo);
|
||||||
if (avail)
|
if (avail)
|
||||||
@ -1175,7 +1228,7 @@ static void ffmpeg_flush_buffers(ffmpeg_t *handle)
|
|||||||
size_t audio_buf_size = handle->config.audio_enable ? (handle->audio.codec->frame_size * handle->params.channels * sizeof(int16_t)) : 0;
|
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;
|
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.
|
/* Try pushing data in an interleaving pattern to ease the work of the muxer a bit. */
|
||||||
bool did_work;
|
bool did_work;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -1208,11 +1261,11 @@ static void ffmpeg_flush_buffers(ffmpeg_t *handle)
|
|||||||
}
|
}
|
||||||
} while (did_work);
|
} while (did_work);
|
||||||
|
|
||||||
// Flush out last audio.
|
/* Flush out last audio. */
|
||||||
if (handle->config.audio_enable)
|
if (handle->config.audio_enable)
|
||||||
ffmpeg_flush_audio(handle, audio_buf, audio_buf_size);
|
ffmpeg_flush_audio(handle, audio_buf, audio_buf_size);
|
||||||
|
|
||||||
// Flush out last video.
|
/* Flush out last video. */
|
||||||
ffmpeg_flush_video(handle);
|
ffmpeg_flush_video(handle);
|
||||||
|
|
||||||
av_free(video_buf);
|
av_free(video_buf);
|
||||||
@ -1228,12 +1281,12 @@ static bool ffmpeg_finalize(void *data)
|
|||||||
|
|
||||||
deinit_thread(handle);
|
deinit_thread(handle);
|
||||||
|
|
||||||
// Flush out data still in buffers (internal, and FFmpeg internal).
|
/* Flush out data still in buffers (internal, and FFmpeg internal). */
|
||||||
ffmpeg_flush_buffers(handle);
|
ffmpeg_flush_buffers(handle);
|
||||||
|
|
||||||
deinit_thread_buf(handle);
|
deinit_thread_buf(handle);
|
||||||
|
|
||||||
// Write final data.
|
/* Write final data. */
|
||||||
av_write_trailer(handle->muxer.ctx);
|
av_write_trailer(handle->muxer.ctx);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1243,7 +1296,7 @@ static void ffmpeg_thread(void *data)
|
|||||||
{
|
{
|
||||||
ffmpeg_t *ff = (ffmpeg_t*)data;
|
ffmpeg_t *ff = (ffmpeg_t*)data;
|
||||||
|
|
||||||
// For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s
|
/* For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. */
|
||||||
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
|
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
|
||||||
assert(video_buf);
|
assert(video_buf);
|
||||||
|
|
||||||
|
376
retroarch.c
376
retroarch.c
@ -54,12 +54,15 @@
|
|||||||
#include "msvc/msvc_compat.h"
|
#include "msvc/msvc_compat.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// To avoid continous switching if we hold the button down, we require that the button must go from pressed,
|
/* To avoid continous switching if we hold the button down, we require
|
||||||
// unpressed back to pressed to be able to toggle between then.
|
* that the button must go from pressed to unpressed back to pressed
|
||||||
|
* to be able to toggle between then.
|
||||||
|
*/
|
||||||
static void check_fast_forward_button(void)
|
static void check_fast_forward_button(void)
|
||||||
{
|
{
|
||||||
bool new_button_state = input_key_pressed_func(RARCH_FAST_FORWARD_KEY);
|
bool new_button_state = input_key_pressed_func(RARCH_FAST_FORWARD_KEY);
|
||||||
bool new_hold_button_state = input_key_pressed_func(RARCH_FAST_FORWARD_HOLD_KEY);
|
bool new_hold_button_state =
|
||||||
|
input_key_pressed_func(RARCH_FAST_FORWARD_HOLD_KEY);
|
||||||
static bool old_button_state = false;
|
static bool old_button_state = false;
|
||||||
static bool old_hold_button_state = false;
|
static bool old_hold_button_state = false;
|
||||||
|
|
||||||
@ -107,7 +110,7 @@ static bool take_screenshot_viewport(void)
|
|||||||
screenshot_dir = screenshot_path;
|
screenshot_dir = screenshot_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data read from viewport is in bottom-up order, suitable for BMP.
|
/* Data read from viewport is in bottom-up order, suitable for BMP. */
|
||||||
if (!screenshot_dump(screenshot_dir, buffer, vp.width, vp.height,
|
if (!screenshot_dump(screenshot_dir, buffer, vp.width, vp.height,
|
||||||
vp.width * 3, true))
|
vp.width * 3, true))
|
||||||
goto done;
|
goto done;
|
||||||
@ -131,12 +134,14 @@ static bool take_screenshot_raw(void)
|
|||||||
|
|
||||||
if (!*g_settings.screenshot_directory)
|
if (!*g_settings.screenshot_directory)
|
||||||
{
|
{
|
||||||
fill_pathname_basedir(screenshot_path, g_extern.basename, sizeof(screenshot_path));
|
fill_pathname_basedir(screenshot_path, g_extern.basename,
|
||||||
|
sizeof(screenshot_path));
|
||||||
screenshot_dir = screenshot_path;
|
screenshot_dir = screenshot_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative pitch is needed as screenshot takes bottom-up,
|
/* Negative pitch is needed as screenshot takes bottom-up,
|
||||||
// but we use top-down.
|
* but we use top-down.
|
||||||
|
*/
|
||||||
return screenshot_dump(screenshot_dir,
|
return screenshot_dump(screenshot_dir,
|
||||||
(const uint8_t*)data + (height - 1) * pitch,
|
(const uint8_t*)data + (height - 1) * pitch,
|
||||||
width, height, -pitch, false);
|
width, height, -pitch, false);
|
||||||
@ -148,7 +153,8 @@ static void take_screenshot(void)
|
|||||||
bool ret = false;
|
bool ret = false;
|
||||||
const char *msg = NULL;
|
const char *msg = NULL;
|
||||||
|
|
||||||
if ((!*g_settings.screenshot_directory) && (!*g_extern.basename)) // No way to infer screenshot directory.
|
/* No way to infer screenshot directory. */
|
||||||
|
if ((!*g_settings.screenshot_directory) && (!*g_extern.basename))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
viewport_read = (g_settings.video.gpu_screenshot ||
|
viewport_read = (g_settings.video.gpu_screenshot ||
|
||||||
@ -156,13 +162,13 @@ static void take_screenshot(void)
|
|||||||
driver.video->read_viewport &&
|
driver.video->read_viewport &&
|
||||||
driver.video->viewport_info;
|
driver.video->viewport_info;
|
||||||
|
|
||||||
// Clear out message queue to avoid OSD fonts to appear on screenshot.
|
/* Clear out message queue to avoid OSD fonts to appear on screenshot. */
|
||||||
msg_queue_clear(g_extern.msg_queue);
|
msg_queue_clear(g_extern.msg_queue);
|
||||||
|
|
||||||
if (viewport_read)
|
if (viewport_read)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MENU
|
#ifdef HAVE_MENU
|
||||||
// Avoid taking screenshot of GUI overlays.
|
/* Avoid taking screenshot of GUI overlays. */
|
||||||
if (driver.video_poke && driver.video_poke->set_texture_enable)
|
if (driver.video_poke && driver.video_poke->set_texture_enable)
|
||||||
driver.video_poke->set_texture_enable(driver.video_data, false, false);
|
driver.video_poke->set_texture_enable(driver.video_data, false, false);
|
||||||
#endif
|
#endif
|
||||||
@ -224,6 +230,7 @@ static void deinit_gpu_recording(void)
|
|||||||
static void init_recording(void)
|
static void init_recording(void)
|
||||||
{
|
{
|
||||||
struct ffemu_params params = {0};
|
struct ffemu_params params = {0};
|
||||||
|
const struct retro_system_av_info *info = {0};
|
||||||
|
|
||||||
if (!g_extern.recording_enable)
|
if (!g_extern.recording_enable)
|
||||||
return;
|
return;
|
||||||
@ -234,15 +241,18 @@ static void init_recording(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_settings.video.gpu_record && g_extern.system.hw_render_callback.context_type)
|
if (!g_settings.video.gpu_record
|
||||||
|
&& g_extern.system.hw_render_callback.context_type)
|
||||||
{
|
{
|
||||||
RARCH_WARN("Libretro core is hardware rendered. Must use post-shaded recording as well.\n");
|
RARCH_WARN("Libretro core is hardware rendered. Must use post-shaded recording as well.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RARCH_LOG("Custom timing given: FPS: %.4f, Sample rate: %.4f\n", (float)g_extern.system.av_info.timing.fps, (float)g_extern.system.av_info.timing.sample_rate);
|
RARCH_LOG("Custom timing given: FPS: %.4f, Sample rate: %.4f\n",
|
||||||
|
(float)g_extern.system.av_info.timing.fps,
|
||||||
|
(float)g_extern.system.av_info.timing.sample_rate);
|
||||||
|
|
||||||
const struct retro_system_av_info *info = (const struct retro_system_av_info*)&g_extern.system.av_info;
|
info = (const struct retro_system_av_info*)&g_extern.system.av_info;
|
||||||
params.out_width = info->geometry.base_width;
|
params.out_width = info->geometry.base_width;
|
||||||
params.out_height = info->geometry.base_height;
|
params.out_height = info->geometry.base_height;
|
||||||
params.fb_width = info->geometry.max_width;
|
params.fb_width = info->geometry.max_width;
|
||||||
@ -251,7 +261,8 @@ static void init_recording(void)
|
|||||||
params.filename = g_extern.record_path;
|
params.filename = g_extern.record_path;
|
||||||
params.fps = g_extern.system.av_info.timing.fps;
|
params.fps = g_extern.system.av_info.timing.fps;
|
||||||
params.samplerate = g_extern.system.av_info.timing.sample_rate;
|
params.samplerate = g_extern.system.av_info.timing.sample_rate;
|
||||||
params.pix_fmt = g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888 ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565;
|
params.pix_fmt = (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888) ?
|
||||||
|
FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565;
|
||||||
params.config = *g_extern.record_config ? g_extern.record_config : NULL;
|
params.config = *g_extern.record_config ? g_extern.record_config : NULL;
|
||||||
|
|
||||||
if (g_settings.video.gpu_record && driver.video->read_viewport)
|
if (g_settings.video.gpu_record && driver.video->read_viewport)
|
||||||
@ -309,7 +320,7 @@ static void init_recording(void)
|
|||||||
unsigned max_width = 0;
|
unsigned max_width = 0;
|
||||||
unsigned max_height = 0;
|
unsigned max_height = 0;
|
||||||
|
|
||||||
params.pix_fmt = g_extern.filter.out_rgb32 ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565;
|
params.pix_fmt = (g_extern.filter.out_rgb32) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565;
|
||||||
|
|
||||||
rarch_softfilter_get_max_output_size(g_extern.filter.filter, &max_width, &max_height);
|
rarch_softfilter_get_max_output_size(g_extern.filter.filter, &max_width, &max_height);
|
||||||
params.fb_width = next_pow2(max_width);
|
params.fb_width = next_pow2(max_width);
|
||||||
@ -346,7 +357,8 @@ static void deinit_recording(void)
|
|||||||
deinit_gpu_recording();
|
deinit_gpu_recording();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void recording_dump_frame(const void *data, unsigned width, unsigned height, size_t pitch)
|
static void recording_dump_frame(const void *data, unsigned width,
|
||||||
|
unsigned height, size_t pitch)
|
||||||
{
|
{
|
||||||
struct ffemu_video_data ffemu_data = {0};
|
struct ffemu_video_data ffemu_data = {0};
|
||||||
|
|
||||||
@ -371,7 +383,7 @@ static void recording_dump_frame(const void *data, unsigned width, unsigned heig
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User has resized. We kinda have a problem now.
|
/* User has resized. We kinda have a problem now. */
|
||||||
if (vp.width != g_extern.record_gpu_width || vp.height != g_extern.record_gpu_height)
|
if (vp.width != g_extern.record_gpu_width || vp.height != g_extern.record_gpu_height)
|
||||||
{
|
{
|
||||||
static const char msg[] = "Recording terminated due to resize.";
|
static const char msg[] = "Recording terminated due to resize.";
|
||||||
@ -383,11 +395,13 @@ static void recording_dump_frame(const void *data, unsigned width, unsigned heig
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big bottleneck.
|
/* Big bottleneck.
|
||||||
// Since we might need to do read-backs asynchronously, it might take 3-4 times
|
* Since we might need to do read-backs asynchronously,
|
||||||
// before this returns true ...
|
* it might take 3-4 times before this returns true ...
|
||||||
|
*/
|
||||||
if (driver.video && driver.video->read_viewport)
|
if (driver.video && driver.video->read_viewport)
|
||||||
if (!driver.video->read_viewport(driver.video_data, g_extern.record_gpu_buffer))
|
if (!driver.video->read_viewport(driver.video_data,
|
||||||
|
g_extern.record_gpu_buffer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ffemu_data.pitch = g_extern.record_gpu_width * 3;
|
ffemu_data.pitch = g_extern.record_gpu_width * 3;
|
||||||
@ -405,7 +419,8 @@ static void recording_dump_frame(const void *data, unsigned width, unsigned heig
|
|||||||
g_extern.rec_driver->push_video(g_extern.rec, &ffemu_data);
|
g_extern.rec_driver->push_video(g_extern.rec, &ffemu_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void video_frame(const void *data, unsigned width, unsigned height, size_t pitch)
|
static void video_frame(const void *data, unsigned width,
|
||||||
|
unsigned height, size_t pitch)
|
||||||
{
|
{
|
||||||
const char *msg = NULL;
|
const char *msg = NULL;
|
||||||
|
|
||||||
@ -417,7 +432,8 @@ static void video_frame(const void *data, unsigned width, unsigned height, size_
|
|||||||
g_extern.frame_cache.height = height;
|
g_extern.frame_cache.height = height;
|
||||||
g_extern.frame_cache.pitch = pitch;
|
g_extern.frame_cache.pitch = pitch;
|
||||||
|
|
||||||
if (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555 && data && data != RETRO_HW_FRAME_BUFFER_VALID)
|
if (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555 &&
|
||||||
|
data && data != RETRO_HW_FRAME_BUFFER_VALID)
|
||||||
{
|
{
|
||||||
RARCH_PERFORMANCE_INIT(video_frame_conv);
|
RARCH_PERFORMANCE_INIT(video_frame_conv);
|
||||||
RARCH_PERFORMANCE_START(video_frame_conv);
|
RARCH_PERFORMANCE_START(video_frame_conv);
|
||||||
@ -434,9 +450,14 @@ static void video_frame(const void *data, unsigned width, unsigned height, size_
|
|||||||
RARCH_PERFORMANCE_STOP(video_frame_conv);
|
RARCH_PERFORMANCE_STOP(video_frame_conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slightly messy code,
|
/* Slightly messy code,
|
||||||
// but we really need to do processing before blocking on VSync for best possible scheduling.
|
* but we really need to do processing before blocking on VSync
|
||||||
if (g_extern.rec && (!g_extern.filter.filter || !g_settings.video.post_filter_record || !data || g_extern.record_gpu_buffer))
|
* for best possible scheduling.
|
||||||
|
*/
|
||||||
|
if (g_extern.rec && (!g_extern.filter.filter
|
||||||
|
|| !g_settings.video.post_filter_record || !data
|
||||||
|
|| g_extern.record_gpu_buffer)
|
||||||
|
)
|
||||||
recording_dump_frame(data, width, height, pitch);
|
recording_dump_frame(data, width, height, pitch);
|
||||||
|
|
||||||
msg = msg_queue_pull(g_extern.msg_queue);
|
msg = msg_queue_pull(g_extern.msg_queue);
|
||||||
@ -484,9 +505,10 @@ void rarch_render_cached_frame(void)
|
|||||||
if (frame == RETRO_HW_FRAME_BUFFER_VALID)
|
if (frame == RETRO_HW_FRAME_BUFFER_VALID)
|
||||||
frame = NULL; /* Dupe */
|
frame = NULL; /* Dupe */
|
||||||
|
|
||||||
// Not 100% safe, since the library might have
|
/* Not 100% safe, since the library might have
|
||||||
// freed the memory, but no known implementations do this :D
|
* freed the memory, but no known implementations do this.
|
||||||
// It would be really stupid at any rate ...
|
* It would be really stupid at any rate ...
|
||||||
|
*/
|
||||||
video_frame(frame,
|
video_frame(frame,
|
||||||
g_extern.frame_cache.width,
|
g_extern.frame_cache.width,
|
||||||
g_extern.frame_cache.height,
|
g_extern.frame_cache.height,
|
||||||
@ -649,7 +671,8 @@ static inline void input_poll_overlay(void)
|
|||||||
for (j = 0; j < ARRAY_SIZE(driver.overlay_state.keys); j++)
|
for (j = 0; j < ARRAY_SIZE(driver.overlay_state.keys); j++)
|
||||||
driver.overlay_state.keys[j] |= polled_data.keys[j];
|
driver.overlay_state.keys[j] |= polled_data.keys[j];
|
||||||
|
|
||||||
// Fingers pressed later take prio and matched up with overlay poll priorities.
|
/* Fingers pressed later take prio and matched up
|
||||||
|
* with overlay poll priorities. */
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
if (polled_data.analog[j])
|
if (polled_data.analog[j])
|
||||||
driver.overlay_state.analog[j] = polled_data.analog[j];
|
driver.overlay_state.analog[j] = polled_data.analog[j];
|
||||||
@ -665,7 +688,8 @@ static inline void input_poll_overlay(void)
|
|||||||
OVERLAY_GET_KEY(&driver.overlay_state, RETROK_RALT)) ? RETROKMOD_ALT : 0;
|
OVERLAY_GET_KEY(&driver.overlay_state, RETROK_RALT)) ? RETROKMOD_ALT : 0;
|
||||||
key_mod |= (OVERLAY_GET_KEY(&driver.overlay_state, RETROK_LMETA) ||
|
key_mod |= (OVERLAY_GET_KEY(&driver.overlay_state, RETROK_LMETA) ||
|
||||||
OVERLAY_GET_KEY(&driver.overlay_state, RETROK_RMETA)) ? RETROKMOD_META : 0;
|
OVERLAY_GET_KEY(&driver.overlay_state, RETROK_RMETA)) ? RETROKMOD_META : 0;
|
||||||
// CAPSLOCK SCROLLOCK NUMLOCK
|
|
||||||
|
/* CAPSLOCK SCROLLOCK NUMLOCK */
|
||||||
for (i = 0; i < ARRAY_SIZE(driver.overlay_state.keys); i++)
|
for (i = 0; i < ARRAY_SIZE(driver.overlay_state.keys); i++)
|
||||||
{
|
{
|
||||||
if (driver.overlay_state.keys[i] != old_key_state.keys[i])
|
if (driver.overlay_state.keys[i] != old_key_state.keys[i])
|
||||||
@ -679,7 +703,7 @@ static inline void input_poll_overlay(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map "analog" buttons to analog axes like regular input drivers do.
|
/* Map "analog" buttons to analog axes like regular input drivers do. */
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
if (!driver.overlay_state.analog[j])
|
if (!driver.overlay_state.analog[j])
|
||||||
@ -691,7 +715,8 @@ static inline void input_poll_overlay(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for analog_dpad_mode. Map analogs to d-pad buttons when configured.
|
/* Check for analog_dpad_mode.
|
||||||
|
* Map analogs to d-pad buttons when configured. */
|
||||||
switch (g_settings.input.analog_dpad_mode[0])
|
switch (g_settings.input.analog_dpad_mode[0])
|
||||||
{
|
{
|
||||||
case ANALOG_DPAD_LSTICK:
|
case ANALOG_DPAD_LSTICK:
|
||||||
@ -735,9 +760,10 @@ void rarch_input_poll(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turbo scheme: If turbo button is held, all buttons pressed except for D-pad will go into
|
/* Turbo scheme: If turbo button is held, all buttons pressed except
|
||||||
// a turbo mode. Until the button is released again, the input state will be modulated by a periodic pulse defined
|
* for D-pad will go into a turbo mode. Until the button is
|
||||||
// by the configured duty cycle.
|
* released again, the input state will be modulated by a periodic pulse
|
||||||
|
* defined by the configured duty cycle. */
|
||||||
static bool input_apply_turbo(unsigned port, unsigned id, bool res)
|
static bool input_apply_turbo(unsigned port, unsigned id, bool res)
|
||||||
{
|
{
|
||||||
if (res && g_extern.turbo_frame_enable[port])
|
if (res && g_extern.turbo_frame_enable[port])
|
||||||
@ -750,7 +776,8 @@ static bool input_apply_turbo(unsigned port, unsigned id, bool res)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int16_t input_state(unsigned port, unsigned device, unsigned index, unsigned id)
|
static int16_t input_state(unsigned port, unsigned device,
|
||||||
|
unsigned index, unsigned id)
|
||||||
{
|
{
|
||||||
int16_t res = 0;
|
int16_t res = 0;
|
||||||
|
|
||||||
@ -793,7 +820,7 @@ static int16_t input_state(unsigned port, unsigned device, unsigned index, unsig
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Don't allow turbo for D-pad.
|
/* Don't allow turbo for D-pad. */
|
||||||
if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP || id > RETRO_DEVICE_ID_JOYPAD_RIGHT))
|
if (device == RETRO_DEVICE_JOYPAD && (id < RETRO_DEVICE_ID_JOYPAD_UP || id > RETRO_DEVICE_ID_JOYPAD_RIGHT))
|
||||||
res = input_apply_turbo(port, id, res);
|
res = input_apply_turbo(port, id, res);
|
||||||
|
|
||||||
@ -959,7 +986,7 @@ static void set_special_paths(char **argv, unsigned num_content)
|
|||||||
unsigned i;
|
unsigned i;
|
||||||
union string_list_elem_attr attr;
|
union string_list_elem_attr attr;
|
||||||
|
|
||||||
// First content file is the significant one.
|
/* First content file is the significant one. */
|
||||||
set_basename(argv[0]);
|
set_basename(argv[0]);
|
||||||
|
|
||||||
g_extern.subsystem_fullpaths = string_list_new();
|
g_extern.subsystem_fullpaths = string_list_new();
|
||||||
@ -970,22 +997,29 @@ static void set_special_paths(char **argv, unsigned num_content)
|
|||||||
for (i = 0; i < num_content; i++)
|
for (i = 0; i < num_content; i++)
|
||||||
string_list_append(g_extern.subsystem_fullpaths, argv[i], attr);
|
string_list_append(g_extern.subsystem_fullpaths, argv[i], attr);
|
||||||
|
|
||||||
// We defer SRAM path updates until we can resolve it.
|
/* We defer SRAM path updates until we can resolve it.
|
||||||
// It is more complicated for special content types.
|
* It is more complicated for special content types.
|
||||||
|
*/
|
||||||
|
|
||||||
if (!g_extern.has_set_state_path)
|
if (!g_extern.has_set_state_path)
|
||||||
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
|
fill_pathname_noext(g_extern.savestate_name, g_extern.basename,
|
||||||
|
".state", sizeof(g_extern.savestate_name));
|
||||||
|
|
||||||
if (path_is_directory(g_extern.savestate_name))
|
if (path_is_directory(g_extern.savestate_name))
|
||||||
{
|
{
|
||||||
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
|
fill_pathname_dir(g_extern.savestate_name, g_extern.basename,
|
||||||
RARCH_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
|
".state", sizeof(g_extern.savestate_name));
|
||||||
|
RARCH_LOG("Redirecting save state to \"%s\".\n",
|
||||||
|
g_extern.savestate_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is already set,
|
/* If this is already set,
|
||||||
// do not overwrite it as this was initialized before in a menu or otherwise.
|
* do not overwrite it as this was initialized before in
|
||||||
|
* a menu or otherwise.
|
||||||
|
*/
|
||||||
if (!*g_settings.system_directory)
|
if (!*g_settings.system_directory)
|
||||||
fill_pathname_basedir(g_settings.system_directory, argv[0], sizeof(g_settings.system_directory));
|
fill_pathname_basedir(g_settings.system_directory, argv[0],
|
||||||
|
sizeof(g_settings.system_directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_paths(const char *path)
|
static void set_paths(const char *path)
|
||||||
@ -993,25 +1027,30 @@ static void set_paths(const char *path)
|
|||||||
set_basename(path);
|
set_basename(path);
|
||||||
|
|
||||||
if (!g_extern.has_set_save_path)
|
if (!g_extern.has_set_save_path)
|
||||||
fill_pathname_noext(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
|
fill_pathname_noext(g_extern.savefile_name, g_extern.basename,
|
||||||
|
".srm", sizeof(g_extern.savefile_name));
|
||||||
if (!g_extern.has_set_state_path)
|
if (!g_extern.has_set_state_path)
|
||||||
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
|
fill_pathname_noext(g_extern.savestate_name, g_extern.basename,
|
||||||
|
".state", sizeof(g_extern.savestate_name));
|
||||||
|
|
||||||
if (path_is_directory(g_extern.savefile_name))
|
if (path_is_directory(g_extern.savefile_name))
|
||||||
{
|
{
|
||||||
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
|
fill_pathname_dir(g_extern.savefile_name, g_extern.basename,
|
||||||
|
".srm", sizeof(g_extern.savefile_name));
|
||||||
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
|
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
|
||||||
}
|
}
|
||||||
if (path_is_directory(g_extern.savestate_name))
|
if (path_is_directory(g_extern.savestate_name))
|
||||||
{
|
{
|
||||||
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
|
fill_pathname_dir(g_extern.savestate_name, g_extern.basename,
|
||||||
|
".state", sizeof(g_extern.savestate_name));
|
||||||
RARCH_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
|
RARCH_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is already set,
|
/* If this is already set, do not overwrite it
|
||||||
// do not overwrite it as this was initialized before in a menu or otherwise.
|
* as this was initialized before in a menu or otherwise. */
|
||||||
if (!*g_settings.system_directory)
|
if (!*g_settings.system_directory)
|
||||||
fill_pathname_basedir(g_settings.system_directory, path, sizeof(g_settings.system_directory));
|
fill_pathname_basedir(g_settings.system_directory, path,
|
||||||
|
sizeof(g_settings.system_directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_input(int argc, char *argv[])
|
static void parse_input(int argc, char *argv[])
|
||||||
@ -1045,7 +1084,7 @@ static void parse_input(int argc, char *argv[])
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can call parse_input several times ...
|
/* Make sure we can call parse_input several times ... */
|
||||||
optind = 0;
|
optind = 0;
|
||||||
|
|
||||||
int val = 0;
|
int val = 0;
|
||||||
@ -1372,7 +1411,7 @@ static void parse_input(int argc, char *argv[])
|
|||||||
else
|
else
|
||||||
g_extern.libretro_no_content = true;
|
g_extern.libretro_no_content = true;
|
||||||
|
|
||||||
// Copy SRM/state dirs used, so they can be reused on reentrancy.
|
/* Copy SRM/state dirs used, so they can be reused on reentrancy. */
|
||||||
if (g_extern.has_set_save_path && path_is_directory(g_extern.savefile_name))
|
if (g_extern.has_set_save_path && path_is_directory(g_extern.savefile_name))
|
||||||
strlcpy(g_extern.savefile_dir, g_extern.savefile_name, sizeof(g_extern.savefile_dir));
|
strlcpy(g_extern.savefile_dir, g_extern.savefile_name, sizeof(g_extern.savefile_dir));
|
||||||
if (g_extern.has_set_state_path && path_is_directory(g_extern.savestate_name))
|
if (g_extern.has_set_state_path && path_is_directory(g_extern.savestate_name))
|
||||||
@ -1397,12 +1436,16 @@ static void init_controllers(void)
|
|||||||
|
|
||||||
if (!ident)
|
if (!ident)
|
||||||
{
|
{
|
||||||
// If we're trying to connect a completely unknown device, revert back to JOYPAD.
|
/* If we're trying to connect a completely unknown device,
|
||||||
|
* revert back to JOYPAD. */
|
||||||
if (device != RETRO_DEVICE_JOYPAD && device != RETRO_DEVICE_NONE)
|
if (device != RETRO_DEVICE_JOYPAD && device != RETRO_DEVICE_NONE)
|
||||||
{
|
{
|
||||||
|
/* Do not fix g_settings.input.libretro_device[i],
|
||||||
|
* because any use of dummy core will reset this,
|
||||||
|
* which is not a good idea.
|
||||||
|
*/
|
||||||
RARCH_WARN("Input device ID %u is unknown to this libretro implementation. Using RETRO_DEVICE_JOYPAD.\n", device);
|
RARCH_WARN("Input device ID %u is unknown to this libretro implementation. Using RETRO_DEVICE_JOYPAD.\n", device);
|
||||||
device = RETRO_DEVICE_JOYPAD;
|
device = RETRO_DEVICE_JOYPAD;
|
||||||
// Do not fix g_settings.input.libretro_device[i], because any use of dummy core will reset this, which is not a good idea.
|
|
||||||
}
|
}
|
||||||
ident = "Joypad";
|
ident = "Joypad";
|
||||||
}
|
}
|
||||||
@ -1414,8 +1457,9 @@ static void init_controllers(void)
|
|||||||
}
|
}
|
||||||
else if (device != RETRO_DEVICE_JOYPAD)
|
else if (device != RETRO_DEVICE_JOYPAD)
|
||||||
{
|
{
|
||||||
// Some cores do not properly range check port argument.
|
/* Some cores do not properly range check port argument.
|
||||||
// This is broken behavior ofc, but avoid breaking cores needlessly.
|
* This is broken behavior ofc, but avoid breaking cores needlessly.
|
||||||
|
*/
|
||||||
RARCH_LOG("Connecting %s (ID: %u) to port %u.\n", ident, device, i + 1);
|
RARCH_LOG("Connecting %s (ID: %u) to port %u.\n", ident, device, i + 1);
|
||||||
pretro_set_controller_port_device(i, device);
|
pretro_set_controller_port_device(i, device);
|
||||||
}
|
}
|
||||||
@ -1430,7 +1474,8 @@ static inline bool load_save_files(void)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 0; i < g_extern.savefiles->size; i++)
|
for (i = 0; i < g_extern.savefiles->size; i++)
|
||||||
load_ram_file(g_extern.savefiles->elems[i].data, g_extern.savefiles->elems[i].attr.i);
|
load_ram_file(g_extern.savefiles->elems[i].data,
|
||||||
|
g_extern.savefiles->elems[i].attr.i);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1513,8 +1558,11 @@ static void init_rewind(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RARCH_LOG("Initializing rewind buffer with size: %u MB\n", (unsigned)(g_settings.rewind_buffer_size / 1000000));
|
RARCH_LOG("Initializing rewind buffer with size: %u MB\n",
|
||||||
g_extern.state_manager = state_manager_new(g_extern.state_size, g_settings.rewind_buffer_size);
|
(unsigned)(g_settings.rewind_buffer_size / 1000000));
|
||||||
|
|
||||||
|
g_extern.state_manager = state_manager_new(g_extern.state_size,
|
||||||
|
g_settings.rewind_buffer_size);
|
||||||
|
|
||||||
if (!g_extern.state_manager)
|
if (!g_extern.state_manager)
|
||||||
RARCH_WARN("Failed to initialize rewind buffer. Rewinding will be disabled.\n");
|
RARCH_WARN("Failed to initialize rewind buffer. Rewinding will be disabled.\n");
|
||||||
@ -1746,8 +1794,12 @@ static void set_savestate_auto_index(void)
|
|||||||
if (!g_settings.savestate_auto_index)
|
if (!g_settings.savestate_auto_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Find the file in the same directory as g_extern.savestate_name with the largest numeral suffix.
|
/* Find the file in the same directory as g_extern.savestate_name
|
||||||
// E.g. /foo/path/content.state, will try to find /foo/path/content.state%d, where %d is the largest number available.
|
* with the largest numeral suffix.
|
||||||
|
*
|
||||||
|
* E.g. /foo/path/content.state, will try to find
|
||||||
|
* /foo/path/content.state%d, where %d is the largest number available.
|
||||||
|
*/
|
||||||
|
|
||||||
fill_pathname_basedir(state_dir, g_extern.savestate_name, sizeof(state_dir));
|
fill_pathname_basedir(state_dir, g_extern.savestate_name, sizeof(state_dir));
|
||||||
fill_pathname_base(state_base, g_extern.savestate_name, sizeof(state_base));
|
fill_pathname_base(state_base, g_extern.savestate_name, sizeof(state_base));
|
||||||
@ -1792,14 +1844,14 @@ static void fill_pathnames(void)
|
|||||||
g_extern.savefiles = string_list_new();
|
g_extern.savefiles = string_list_new();
|
||||||
rarch_assert(g_extern.savefiles);
|
rarch_assert(g_extern.savefiles);
|
||||||
|
|
||||||
// For subsystems, we know exactly which RAM types are supported.
|
/* For subsystems, we know exactly which RAM types are supported. */
|
||||||
if (*g_extern.subsystem)
|
if (*g_extern.subsystem)
|
||||||
{
|
{
|
||||||
unsigned i, j;
|
unsigned i, j;
|
||||||
const struct retro_subsystem_info *info =
|
const struct retro_subsystem_info *info =
|
||||||
(const struct retro_subsystem_info*)libretro_find_subsystem_info(g_extern.system.special, g_extern.system.num_special, g_extern.subsystem);
|
(const struct retro_subsystem_info*)libretro_find_subsystem_info(g_extern.system.special, g_extern.system.num_special, g_extern.subsystem);
|
||||||
|
|
||||||
// We'll handle this error gracefully later.
|
/* We'll handle this error gracefully later. */
|
||||||
unsigned num_content = min(info ? info->num_roms : 0, g_extern.subsystem_fullpaths ? g_extern.subsystem_fullpaths->size : 0);
|
unsigned num_content = min(info ? info->num_roms : 0, g_extern.subsystem_fullpaths ? g_extern.subsystem_fullpaths->size : 0);
|
||||||
|
|
||||||
bool use_sram_dir = path_is_directory(g_extern.savefile_name);
|
bool use_sram_dir = path_is_directory(g_extern.savefile_name);
|
||||||
@ -1816,7 +1868,7 @@ static void fill_pathnames(void)
|
|||||||
|
|
||||||
if (use_sram_dir)
|
if (use_sram_dir)
|
||||||
{
|
{
|
||||||
// Redirect content fullpath to save directory.
|
/* Redirect content fullpath to save directory. */
|
||||||
strlcpy(path, g_extern.savefile_name, sizeof(path));
|
strlcpy(path, g_extern.savefile_name, sizeof(path));
|
||||||
fill_pathname_dir(path, g_extern.subsystem_fullpaths->elems[i].data, ext,
|
fill_pathname_dir(path, g_extern.subsystem_fullpaths->elems[i].data, ext,
|
||||||
sizeof(path));
|
sizeof(path));
|
||||||
@ -1832,12 +1884,14 @@ static void fill_pathnames(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let other relevant paths be inferred from the main SRAM location.
|
/* Let other relevant paths be inferred from the main SRAM location. */
|
||||||
if (!g_extern.has_set_save_path)
|
if (!g_extern.has_set_save_path)
|
||||||
fill_pathname_noext(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
|
fill_pathname_noext(g_extern.savefile_name, g_extern.basename, ".srm",
|
||||||
|
sizeof(g_extern.savefile_name));
|
||||||
if (path_is_directory(g_extern.savefile_name))
|
if (path_is_directory(g_extern.savefile_name))
|
||||||
{
|
{
|
||||||
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm", sizeof(g_extern.savefile_name));
|
fill_pathname_dir(g_extern.savefile_name, g_extern.basename, ".srm",
|
||||||
|
sizeof(g_extern.savefile_name));
|
||||||
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
|
RARCH_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1849,23 +1903,27 @@ static void fill_pathnames(void)
|
|||||||
attr.i = RETRO_MEMORY_SAVE_RAM;
|
attr.i = RETRO_MEMORY_SAVE_RAM;
|
||||||
string_list_append(g_extern.savefiles, g_extern.savefile_name, attr);
|
string_list_append(g_extern.savefiles, g_extern.savefile_name, attr);
|
||||||
|
|
||||||
// Infer .rtc save path from save ram path.
|
/* Infer .rtc save path from save ram path. */
|
||||||
attr.i = RETRO_MEMORY_RTC;
|
attr.i = RETRO_MEMORY_RTC;
|
||||||
fill_pathname(savefile_name_rtc,
|
fill_pathname(savefile_name_rtc,
|
||||||
g_extern.savefile_name, ".rtc", sizeof(savefile_name_rtc));
|
g_extern.savefile_name, ".rtc", sizeof(savefile_name_rtc));
|
||||||
string_list_append(g_extern.savefiles, savefile_name_rtc, attr);
|
string_list_append(g_extern.savefiles, savefile_name_rtc, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name, "", sizeof(g_extern.bsv.movie_path));
|
fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name, "",
|
||||||
|
sizeof(g_extern.bsv.movie_path));
|
||||||
|
|
||||||
if (*g_extern.basename)
|
if (*g_extern.basename)
|
||||||
{
|
{
|
||||||
if (!*g_extern.ups_name)
|
if (!*g_extern.ups_name)
|
||||||
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups", sizeof(g_extern.ups_name));
|
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups",
|
||||||
|
sizeof(g_extern.ups_name));
|
||||||
if (!*g_extern.bps_name)
|
if (!*g_extern.bps_name)
|
||||||
fill_pathname_noext(g_extern.bps_name, g_extern.basename, ".bps", sizeof(g_extern.bps_name));
|
fill_pathname_noext(g_extern.bps_name, g_extern.basename, ".bps",
|
||||||
|
sizeof(g_extern.bps_name));
|
||||||
if (!*g_extern.ips_name)
|
if (!*g_extern.ips_name)
|
||||||
fill_pathname_noext(g_extern.ips_name, g_extern.basename, ".ips", sizeof(g_extern.ips_name));
|
fill_pathname_noext(g_extern.ips_name, g_extern.basename, ".ips",
|
||||||
|
sizeof(g_extern.ips_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1916,9 +1974,9 @@ static bool save_auto_state(void)
|
|||||||
|
|
||||||
static void rarch_load_state(void)
|
static void rarch_load_state(void)
|
||||||
{
|
{
|
||||||
char load_path[PATH_MAX], msg[512];
|
char load_path[PATH_MAX], msg[PATH_MAX];
|
||||||
|
|
||||||
// Disallow savestate load when we absolutely cannot change game state.
|
/* Disallow savestate load when we absolutely cannot change game state. */
|
||||||
if (g_extern.bsv.movie)
|
if (g_extern.bsv.movie)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1928,23 +1986,29 @@ static void rarch_load_state(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (g_settings.state_slot > 0)
|
if (g_settings.state_slot > 0)
|
||||||
snprintf(load_path, sizeof(load_path), "%s%d", g_extern.savestate_name, g_settings.state_slot);
|
snprintf(load_path, sizeof(load_path), "%s%d",
|
||||||
|
g_extern.savestate_name, g_settings.state_slot);
|
||||||
else if (g_settings.state_slot < 0)
|
else if (g_settings.state_slot < 0)
|
||||||
snprintf(load_path, sizeof(load_path), "%s.auto", g_extern.savestate_name);
|
snprintf(load_path, sizeof(load_path), "%s.auto",
|
||||||
|
g_extern.savestate_name);
|
||||||
else
|
else
|
||||||
snprintf(load_path, sizeof(load_path), "%s", g_extern.savestate_name);
|
snprintf(load_path, sizeof(load_path), "%s",
|
||||||
|
g_extern.savestate_name);
|
||||||
|
|
||||||
if (pretro_serialize_size())
|
if (pretro_serialize_size())
|
||||||
{
|
{
|
||||||
if (load_state(load_path))
|
if (load_state(load_path))
|
||||||
{
|
{
|
||||||
if (g_settings.state_slot < 0)
|
if (g_settings.state_slot < 0)
|
||||||
snprintf(msg, sizeof(msg), "Loaded state from slot #-1 (auto).");
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Loaded state from slot #-1 (auto).");
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Loaded state from slot #%d.", g_settings.state_slot);
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Loaded state from slot #%d.", g_settings.state_slot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Failed to load state from \"%s\".", load_path);
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Failed to load state from \"%s\".", load_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
strlcpy(msg, "Core does not support save states.", sizeof(msg));
|
strlcpy(msg, "Core does not support save states.", sizeof(msg));
|
||||||
@ -1962,23 +2026,29 @@ static void rarch_save_state(void)
|
|||||||
g_settings.state_slot++;
|
g_settings.state_slot++;
|
||||||
|
|
||||||
if (g_settings.state_slot > 0)
|
if (g_settings.state_slot > 0)
|
||||||
snprintf(save_path, sizeof(save_path), "%s%d", g_extern.savestate_name, g_settings.state_slot);
|
snprintf(save_path, sizeof(save_path), "%s%d",
|
||||||
|
g_extern.savestate_name, g_settings.state_slot);
|
||||||
else if (g_settings.state_slot < 0)
|
else if (g_settings.state_slot < 0)
|
||||||
snprintf(save_path, sizeof(save_path), "%s.auto", g_extern.savestate_name);
|
snprintf(save_path, sizeof(save_path), "%s.auto",
|
||||||
|
g_extern.savestate_name);
|
||||||
else
|
else
|
||||||
snprintf(save_path, sizeof(save_path), "%s", g_extern.savestate_name);
|
snprintf(save_path, sizeof(save_path), "%s",
|
||||||
|
g_extern.savestate_name);
|
||||||
|
|
||||||
if (pretro_serialize_size())
|
if (pretro_serialize_size())
|
||||||
{
|
{
|
||||||
if (save_state(save_path))
|
if (save_state(save_path))
|
||||||
{
|
{
|
||||||
if (g_settings.state_slot < 0)
|
if (g_settings.state_slot < 0)
|
||||||
snprintf(msg, sizeof(msg), "Saved state to slot #-1 (auto).");
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Saved state to slot #-1 (auto).");
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Saved state to slot #%u.", g_settings.state_slot);
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Saved state to slot #%u.", g_settings.state_slot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Failed to save state to \"%s\".", save_path);
|
snprintf(msg, sizeof(msg),
|
||||||
|
"Failed to save state to \"%s\".", save_path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
strlcpy(msg, "Core does not support save states.", sizeof(msg));
|
strlcpy(msg, "Core does not support save states.", sizeof(msg));
|
||||||
@ -2018,14 +2088,14 @@ static void set_fullscreen(bool fullscreen)
|
|||||||
init_drivers();
|
init_drivers();
|
||||||
driver.video_cache_context = false;
|
driver.video_cache_context = false;
|
||||||
|
|
||||||
// Poll input to avoid possibly stale data to corrupt things.
|
/* Poll input to avoid possibly stale data to corrupt things. */
|
||||||
if (driver.input)
|
if (driver.input)
|
||||||
driver.input->poll(driver.input_data);
|
driver.input->poll(driver.input_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rarch_check_fullscreen(void)
|
bool rarch_check_fullscreen(void)
|
||||||
{
|
{
|
||||||
// If we go fullscreen we drop all drivers and reinitialize to be safe.
|
/* If we go fullscreen we drop all drivers and reinitialize to be safe. */
|
||||||
static bool was_pressed = false;
|
static bool was_pressed = false;
|
||||||
bool pressed = input_key_pressed_func(RARCH_FULLSCREEN_TOGGLE_KEY);
|
bool pressed = input_key_pressed_func(RARCH_FULLSCREEN_TOGGLE_KEY);
|
||||||
bool toggle = pressed && !was_pressed;
|
bool toggle = pressed && !was_pressed;
|
||||||
@ -2042,7 +2112,7 @@ bool rarch_check_fullscreen(void)
|
|||||||
|
|
||||||
static void rarch_state_slot_increase(void)
|
static void rarch_state_slot_increase(void)
|
||||||
{
|
{
|
||||||
char msg[256];
|
char msg[PATH_MAX];
|
||||||
|
|
||||||
g_settings.state_slot++;
|
g_settings.state_slot++;
|
||||||
|
|
||||||
@ -2059,7 +2129,7 @@ static void rarch_state_slot_increase(void)
|
|||||||
|
|
||||||
static void rarch_state_slot_decrease(void)
|
static void rarch_state_slot_decrease(void)
|
||||||
{
|
{
|
||||||
char msg[256];
|
char msg[PATH_MAX];
|
||||||
|
|
||||||
if (g_settings.state_slot > 0)
|
if (g_settings.state_slot > 0)
|
||||||
g_settings.state_slot--;
|
g_settings.state_slot--;
|
||||||
@ -2098,9 +2168,11 @@ static inline void flush_rewind_audio(void)
|
|||||||
if (!g_extern.frame_is_reverse)
|
if (!g_extern.frame_is_reverse)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We just rewound. Flush rewind audio buffer.
|
/* We just rewound. Flush rewind audio buffer. */
|
||||||
g_extern.audio_active = audio_flush(g_extern.audio_data.rewind_buf + g_extern.audio_data.rewind_ptr,
|
g_extern.audio_active = audio_flush(g_extern.audio_data.rewind_buf
|
||||||
g_extern.audio_data.rewind_size - g_extern.audio_data.rewind_ptr) && g_extern.audio_active;
|
+ g_extern.audio_data.rewind_ptr,
|
||||||
|
g_extern.audio_data.rewind_size - g_extern.audio_data.rewind_ptr)
|
||||||
|
&& g_extern.audio_active;
|
||||||
|
|
||||||
g_extern.frame_is_reverse = false;
|
g_extern.frame_is_reverse = false;
|
||||||
}
|
}
|
||||||
@ -2109,7 +2181,7 @@ static inline void setup_rewind_audio(void)
|
|||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
// Push audio ready to be played.
|
/* Push audio ready to be played. */
|
||||||
g_extern.audio_data.rewind_ptr = g_extern.audio_data.rewind_size;
|
g_extern.audio_data.rewind_ptr = g_extern.audio_data.rewind_size;
|
||||||
|
|
||||||
for (i = 0; i < g_extern.audio_data.data_ptr; i += 2)
|
for (i = 0; i < g_extern.audio_data.data_ptr; i += 2)
|
||||||
@ -2149,20 +2221,23 @@ static void check_rewind(void)
|
|||||||
g_extern.frame_is_reverse = true;
|
g_extern.frame_is_reverse = true;
|
||||||
setup_rewind_audio();
|
setup_rewind_audio();
|
||||||
|
|
||||||
msg_queue_push(g_extern.msg_queue, "Rewinding.", 0, g_extern.is_paused ? 1 : 30);
|
msg_queue_push(g_extern.msg_queue, "Rewinding.", 0,
|
||||||
|
g_extern.is_paused ? 1 : 30);
|
||||||
pretro_unserialize(buf, g_extern.state_size);
|
pretro_unserialize(buf, g_extern.state_size);
|
||||||
|
|
||||||
if (g_extern.bsv.movie)
|
if (g_extern.bsv.movie)
|
||||||
bsv_movie_frame_rewind(g_extern.bsv.movie);
|
bsv_movie_frame_rewind(g_extern.bsv.movie);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer.", 0, 30);
|
msg_queue_push(g_extern.msg_queue,
|
||||||
|
"Reached end of rewind buffer.", 0, 30);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static unsigned cnt = 0;
|
static unsigned cnt = 0;
|
||||||
|
|
||||||
cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE.
|
cnt = (cnt + 1) % (g_settings.rewind_granularity ?
|
||||||
|
g_settings.rewind_granularity : 1); /* Avoid possible SIGFPE. */
|
||||||
|
|
||||||
if ((cnt == 0) || g_extern.bsv.movie)
|
if ((cnt == 0) || g_extern.bsv.movie)
|
||||||
{
|
{
|
||||||
@ -2192,7 +2267,8 @@ static void check_slowmotion(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
msg_queue_clear(g_extern.msg_queue);
|
msg_queue_clear(g_extern.msg_queue);
|
||||||
msg_queue_push(g_extern.msg_queue, g_extern.frame_is_reverse ? "Slow motion rewind." : "Slow motion.", 0, 30);
|
msg_queue_push(g_extern.msg_queue, g_extern.frame_is_reverse ?
|
||||||
|
"Slow motion rewind." : "Slow motion.", 0, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_movie_record(bool pressed)
|
static void check_movie_record(bool pressed)
|
||||||
@ -2228,7 +2304,8 @@ static void check_movie_record(bool pressed)
|
|||||||
|
|
||||||
g_extern.bsv.movie = bsv_movie_init(path, RARCH_MOVIE_RECORD);
|
g_extern.bsv.movie = bsv_movie_init(path, RARCH_MOVIE_RECORD);
|
||||||
msg_queue_clear(g_extern.msg_queue);
|
msg_queue_clear(g_extern.msg_queue);
|
||||||
msg_queue_push(g_extern.msg_queue, g_extern.bsv.movie ? msg : "Failed to start movie record.", 1, 180);
|
msg_queue_push(g_extern.msg_queue, g_extern.bsv.movie ?
|
||||||
|
msg : "Failed to start movie record.", 1, 180);
|
||||||
|
|
||||||
if (g_extern.bsv.movie)
|
if (g_extern.bsv.movie)
|
||||||
RARCH_LOG("Starting movie record to \"%s\".\n", path);
|
RARCH_LOG("Starting movie record to \"%s\".\n", path);
|
||||||
@ -2273,8 +2350,9 @@ static void check_pause(void)
|
|||||||
bool has_set_audio_start = false;
|
bool has_set_audio_start = false;
|
||||||
bool new_state = input_key_pressed_func(RARCH_PAUSE_TOGGLE);
|
bool new_state = input_key_pressed_func(RARCH_PAUSE_TOGGLE);
|
||||||
|
|
||||||
// FRAMEADVANCE will set us into pause mode.
|
/* FRAMEADVANCE will set us into pause mode. */
|
||||||
new_state |= !g_extern.is_paused && input_key_pressed_func(RARCH_FRAMEADVANCE);
|
new_state |= !g_extern.is_paused &&
|
||||||
|
input_key_pressed_func(RARCH_FRAMEADVANCE);
|
||||||
|
|
||||||
if (g_settings.pause_nonactive)
|
if (g_settings.pause_nonactive)
|
||||||
focus = driver.video->focus(driver.video_data);
|
focus = driver.video->focus(driver.video_data);
|
||||||
@ -2326,7 +2404,8 @@ static void check_oneshot(void)
|
|||||||
g_extern.is_oneshot = (new_state && !old_state);
|
g_extern.is_oneshot = (new_state && !old_state);
|
||||||
old_state = new_state;
|
old_state = new_state;
|
||||||
|
|
||||||
// Rewind buttons works like FRAMEREWIND when paused. We will one-shot in that case.
|
/* Rewind buttons works like FRAMEREWIND when paused.
|
||||||
|
* We will one-shot in that case. */
|
||||||
g_extern.is_oneshot |= new_rewind_state && !old_rewind_state;
|
g_extern.is_oneshot |= new_rewind_state && !old_rewind_state;
|
||||||
old_rewind_state = new_rewind_state;
|
old_rewind_state = new_rewind_state;
|
||||||
}
|
}
|
||||||
@ -2360,12 +2439,14 @@ static void check_turbo(void)
|
|||||||
|
|
||||||
|
|
||||||
if (driver.block_libretro_input)
|
if (driver.block_libretro_input)
|
||||||
memset(g_extern.turbo_frame_enable, 0, sizeof(g_extern.turbo_frame_enable));
|
memset(g_extern.turbo_frame_enable, 0,
|
||||||
|
sizeof(g_extern.turbo_frame_enable));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < MAX_PLAYERS; i++)
|
for (i = 0; i < MAX_PLAYERS; i++)
|
||||||
g_extern.turbo_frame_enable[i] =
|
g_extern.turbo_frame_enable[i] =
|
||||||
driver.input->input_state(driver.input_data, binds, i, RETRO_DEVICE_JOYPAD, 0, RARCH_TURBO_ENABLE);
|
driver.input->input_state(driver.input_data, binds, i,
|
||||||
|
RETRO_DEVICE_JOYPAD, 0, RARCH_TURBO_ENABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2483,13 +2564,14 @@ void rarch_disk_control_append_image(const char *path)
|
|||||||
deinit_autosave();
|
deinit_autosave();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Need to figure out what to do with subsystems case.
|
/* TODO: Need to figure out what to do with subsystems case. */
|
||||||
if (!*g_extern.subsystem)
|
if (!*g_extern.subsystem)
|
||||||
{
|
{
|
||||||
// Update paths for our new image.
|
/* Update paths for our new image.
|
||||||
// If we actually use append_image,
|
* If we actually use append_image,
|
||||||
// we assume that we started out in a single disk case,
|
* we assume that we started out in a single disk case,
|
||||||
// and that this way of doing it makes the most sense.
|
* and that this way of doing it makes the most sense.
|
||||||
|
*/
|
||||||
set_paths(path);
|
set_paths(path);
|
||||||
fill_pathnames();
|
fill_pathnames();
|
||||||
}
|
}
|
||||||
@ -2514,11 +2596,13 @@ void rarch_disk_control_set_eject(bool new_state, bool log)
|
|||||||
*msg = '\0';
|
*msg = '\0';
|
||||||
|
|
||||||
if (control->set_eject_state(new_state))
|
if (control->set_eject_state(new_state))
|
||||||
snprintf(msg, sizeof(msg), "%s virtual disk tray.", new_state ? "Ejected" : "Closed");
|
snprintf(msg, sizeof(msg), "%s virtual disk tray.",
|
||||||
|
new_state ? "Ejected" : "Closed");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error = true;
|
error = true;
|
||||||
snprintf(msg, sizeof(msg), "Failed to %s virtual disk tray.", new_state ? "eject" : "close");
|
snprintf(msg, sizeof(msg), "Failed to %s virtual disk tray.",
|
||||||
|
new_state ? "eject" : "close");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*msg)
|
if (*msg)
|
||||||
@ -2528,7 +2612,7 @@ void rarch_disk_control_set_eject(bool new_state, bool log)
|
|||||||
else
|
else
|
||||||
RARCH_LOG("%s\n", msg);
|
RARCH_LOG("%s\n", msg);
|
||||||
|
|
||||||
// Only noise in menu.
|
/* Only noise in menu. */
|
||||||
if (log)
|
if (log)
|
||||||
{
|
{
|
||||||
msg_queue_clear(g_extern.msg_queue);
|
msg_queue_clear(g_extern.msg_queue);
|
||||||
@ -2555,14 +2639,16 @@ void rarch_disk_control_set_index(unsigned next_index)
|
|||||||
if (control->set_image_index(next_index))
|
if (control->set_image_index(next_index))
|
||||||
{
|
{
|
||||||
if (next_index < num_disks)
|
if (next_index < num_disks)
|
||||||
snprintf(msg, sizeof(msg), "Setting disk %u of %u in tray.", next_index + 1, num_disks);
|
snprintf(msg, sizeof(msg), "Setting disk %u of %u in tray.",
|
||||||
|
next_index + 1, num_disks);
|
||||||
else
|
else
|
||||||
strlcpy(msg, "Removed disk from tray.", sizeof(msg));
|
strlcpy(msg, "Removed disk from tray.", sizeof(msg));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (next_index < num_disks)
|
if (next_index < num_disks)
|
||||||
snprintf(msg, sizeof(msg), "Failed to set disk %u of %u.", next_index + 1, num_disks);
|
snprintf(msg, sizeof(msg), "Failed to set disk %u of %u.",
|
||||||
|
next_index + 1, num_disks);
|
||||||
else
|
else
|
||||||
strlcpy(msg, "Failed to remove disk from tray.", sizeof(msg));
|
strlcpy(msg, "Failed to remove disk from tray.", sizeof(msg));
|
||||||
error = true;
|
error = true;
|
||||||
@ -2605,8 +2691,9 @@ static void check_disk(void)
|
|||||||
unsigned current = control->get_image_index();
|
unsigned current = control->get_image_index();
|
||||||
if (num_disks && num_disks != UINT_MAX)
|
if (num_disks && num_disks != UINT_MAX)
|
||||||
{
|
{
|
||||||
// Use "no disk" state when index == num_disks.
|
/* Use "no disk" state when index == num_disks. */
|
||||||
unsigned next_index = current >= num_disks ? 0 : ((current + 1) % (num_disks + 1));
|
unsigned next_index = current >= num_disks ?
|
||||||
|
0 : ((current + 1) % (num_disks + 1));
|
||||||
rarch_disk_control_set_index(next_index);
|
rarch_disk_control_set_index(next_index);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2640,7 +2727,8 @@ static void check_mute(void)
|
|||||||
{
|
{
|
||||||
g_extern.audio_data.mute = !g_extern.audio_data.mute;
|
g_extern.audio_data.mute = !g_extern.audio_data.mute;
|
||||||
|
|
||||||
const char *msg = g_extern.audio_data.mute ? "Audio muted." : "Audio unmuted.";
|
const char *msg = g_extern.audio_data.mute ?
|
||||||
|
"Audio muted." : "Audio unmuted.";
|
||||||
msg_queue_clear(g_extern.msg_queue);
|
msg_queue_clear(g_extern.msg_queue);
|
||||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||||
|
|
||||||
@ -2709,16 +2797,19 @@ void rarch_check_block_hotkey(void)
|
|||||||
static const struct retro_keybind *bind = &g_settings.input.binds[0][RARCH_ENABLE_HOTKEY];
|
static const struct retro_keybind *bind = &g_settings.input.binds[0][RARCH_ENABLE_HOTKEY];
|
||||||
bool use_hotkey_enable, enable_hotkey;
|
bool use_hotkey_enable, enable_hotkey;
|
||||||
|
|
||||||
// Don't block the check to RARCH_ENABLE_HOTKEY unless we're really supposed to.
|
/* Don't block the check to RARCH_ENABLE_HOTKEY
|
||||||
|
* unless we're really supposed to. */
|
||||||
driver.block_hotkey = driver.block_input;
|
driver.block_hotkey = driver.block_input;
|
||||||
|
|
||||||
// If we haven't bound anything to this, always allow hotkeys.
|
// If we haven't bound anything to this, always allow hotkeys.
|
||||||
use_hotkey_enable = bind->key != RETROK_UNKNOWN || bind->joykey != NO_BTN || bind->joyaxis != AXIS_NONE;
|
use_hotkey_enable = bind->key != RETROK_UNKNOWN || bind->joykey != NO_BTN ||
|
||||||
|
bind->joyaxis != AXIS_NONE;
|
||||||
enable_hotkey = input_key_pressed_func(RARCH_ENABLE_HOTKEY);
|
enable_hotkey = input_key_pressed_func(RARCH_ENABLE_HOTKEY);
|
||||||
|
|
||||||
driver.block_hotkey = driver.block_input || (use_hotkey_enable && !enable_hotkey);
|
driver.block_hotkey = driver.block_input || (use_hotkey_enable && !enable_hotkey);
|
||||||
|
|
||||||
// If we hold ENABLE_HOTKEY button, block all libretro input to allow hotkeys to be bound to same keys as RetroPad.
|
/* If we hold ENABLE_HOTKEY button, block all libretro input to allow
|
||||||
|
* hotkeys to be bound to same keys as RetroPad. */
|
||||||
driver.block_libretro_input = use_hotkey_enable && enable_hotkey;
|
driver.block_libretro_input = use_hotkey_enable && enable_hotkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2864,7 +2955,8 @@ void rarch_init_system_info(void)
|
|||||||
snprintf(g_extern.title_buf, sizeof(g_extern.title_buf), "RetroArch : %s %s",
|
snprintf(g_extern.title_buf, sizeof(g_extern.title_buf), "RetroArch : %s %s",
|
||||||
info->library_name, info->library_version);
|
info->library_name, info->library_version);
|
||||||
#endif
|
#endif
|
||||||
strlcpy(g_extern.system.valid_extensions, info->valid_extensions ? info->valid_extensions : DEFAULT_EXT,
|
strlcpy(g_extern.system.valid_extensions, info->valid_extensions ?
|
||||||
|
info->valid_extensions : DEFAULT_EXT,
|
||||||
sizeof(g_extern.system.valid_extensions));
|
sizeof(g_extern.system.valid_extensions));
|
||||||
g_extern.system.block_extract = info->block_extract;
|
g_extern.system.block_extract = info->block_extract;
|
||||||
}
|
}
|
||||||
@ -2883,8 +2975,9 @@ static void verify_api_version(void)
|
|||||||
RARCH_WARN("RetroArch is compiled against a different version of libretro than this libretro implementation.\n");
|
RARCH_WARN("RetroArch is compiled against a different version of libretro than this libretro implementation.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we haven't compiled for something we cannot run.
|
/* Make sure we haven't compiled for something we cannot run.
|
||||||
// Ideally, code would get swapped out depending on CPU support, but this will do for now.
|
* Ideally, code would get swapped out depending on CPU support, but this will do for now.
|
||||||
|
*/
|
||||||
static void validate_cpu_features(void)
|
static void validate_cpu_features(void)
|
||||||
{
|
{
|
||||||
uint64_t cpu = rarch_get_cpu_features();
|
uint64_t cpu = rarch_get_cpu_features();
|
||||||
@ -3033,7 +3126,7 @@ static inline bool check_enter_menu(void)
|
|||||||
bool rmenu_toggle = input_key_pressed_func(RARCH_MENU_TOGGLE)
|
bool rmenu_toggle = input_key_pressed_func(RARCH_MENU_TOGGLE)
|
||||||
|| (g_extern.libretro_dummy && !old_rmenu_toggle);
|
|| (g_extern.libretro_dummy && !old_rmenu_toggle);
|
||||||
|
|
||||||
// Always go into menu if dummy core is loaded.
|
/* Always go into menu if dummy core is loaded. */
|
||||||
if (rmenu_toggle && !old_rmenu_toggle)
|
if (rmenu_toggle && !old_rmenu_toggle)
|
||||||
{
|
{
|
||||||
g_extern.lifecycle_state |= (1ULL << MODE_MENU_PREINIT);
|
g_extern.lifecycle_state |= (1ULL << MODE_MENU_PREINIT);
|
||||||
@ -3080,7 +3173,9 @@ static inline void limit_frame_time(void)
|
|||||||
if (g_settings.fastforward_ratio < 0.0f)
|
if (g_settings.fastforward_ratio < 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_extern.frame_limit.minimum_frame_time = (retro_time_t)roundf(1000000.0f / (g_extern.system.av_info.timing.fps * g_settings.fastforward_ratio));
|
g_extern.frame_limit.minimum_frame_time = (retro_time_t)
|
||||||
|
roundf(1000000.0f / (g_extern.system.av_info.timing.fps *
|
||||||
|
g_settings.fastforward_ratio));
|
||||||
|
|
||||||
retro_time_t current = rarch_get_time_usec();
|
retro_time_t current = rarch_get_time_usec();
|
||||||
retro_time_t target = g_extern.frame_limit.last_frame_time + g_extern.frame_limit.minimum_frame_time;
|
retro_time_t target = g_extern.frame_limit.last_frame_time + g_extern.frame_limit.minimum_frame_time;
|
||||||
@ -3094,8 +3189,9 @@ static inline void limit_frame_time(void)
|
|||||||
g_extern.frame_limit.last_frame_time = rarch_get_time_usec();
|
g_extern.frame_limit.last_frame_time = rarch_get_time_usec();
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO - can we refactor command.c to do this? Should be local and not
|
/*TODO - can we refactor command.c to do this? Should be local and not
|
||||||
//stdin or network-based
|
* stdin or network-based
|
||||||
|
*/
|
||||||
|
|
||||||
void rarch_main_command(unsigned action)
|
void rarch_main_command(unsigned action)
|
||||||
{
|
{
|
||||||
@ -3274,16 +3370,16 @@ bool rarch_main_iterate(void)
|
|||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
// SHUTDOWN on consoles should exit RetroArch completely.
|
/* SHUTDOWN on consoles should exit RetroArch completely. */
|
||||||
if (g_extern.system.shutdown)
|
if (g_extern.system.shutdown)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Time to drop?
|
/* Time to drop? */
|
||||||
if (input_key_pressed_func(RARCH_QUIT_KEY) || !driver.video->alive(driver.video_data))
|
if (input_key_pressed_func(RARCH_QUIT_KEY) || !driver.video->alive(driver.video_data))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (check_enter_menu())
|
if (check_enter_menu())
|
||||||
return false; // Enter menu, don't exit.
|
return false; /* Enter menu, don't exit. */
|
||||||
|
|
||||||
if (g_extern.exec)
|
if (g_extern.exec)
|
||||||
{
|
{
|
||||||
@ -3291,7 +3387,7 @@ bool rarch_main_iterate(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for stuff like fullscreen, save states, etc.
|
/* Checks for stuff like fullscreen, save states, etc. */
|
||||||
do_state_checks();
|
do_state_checks();
|
||||||
|
|
||||||
if (g_extern.is_paused && !g_extern.is_oneshot)
|
if (g_extern.is_paused && !g_extern.is_oneshot)
|
||||||
@ -3301,7 +3397,7 @@ bool rarch_main_iterate(void)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run libretro for one frame.
|
/* Run libretro for one frame. */
|
||||||
#if defined(HAVE_THREADS)
|
#if defined(HAVE_THREADS)
|
||||||
lock_autosave();
|
lock_autosave();
|
||||||
#endif
|
#endif
|
||||||
@ -3317,11 +3413,13 @@ bool rarch_main_iterate(void)
|
|||||||
if (g_extern.system.camera_callback.caps)
|
if (g_extern.system.camera_callback.caps)
|
||||||
driver_camera_poll();
|
driver_camera_poll();
|
||||||
|
|
||||||
// Update binds for analog dpad modes.
|
/* Update binds for analog dpad modes. */
|
||||||
for (i = 0; i < MAX_PLAYERS; i++)
|
for (i = 0; i < MAX_PLAYERS; i++)
|
||||||
{
|
{
|
||||||
input_push_analog_dpad(g_settings.input.binds[i], g_settings.input.analog_dpad_mode[i]);
|
input_push_analog_dpad(g_settings.input.binds[i],
|
||||||
input_push_analog_dpad(g_settings.input.autoconf_binds[i], g_settings.input.analog_dpad_mode[i]);
|
g_settings.input.analog_dpad_mode[i]);
|
||||||
|
input_push_analog_dpad(g_settings.input.autoconf_binds[i],
|
||||||
|
g_settings.input.analog_dpad_mode[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_frame_time();
|
update_frame_time();
|
||||||
|
@ -37,7 +37,7 @@ static bool write_header_bmp(FILE *file, unsigned width, unsigned height)
|
|||||||
unsigned size = line_size * height + 54;
|
unsigned size = line_size * height + 54;
|
||||||
unsigned size_array = line_size * height;
|
unsigned size_array = line_size * height;
|
||||||
|
|
||||||
// Generic BMP stuff.
|
/* Generic BMP stuff. */
|
||||||
const uint8_t header[] = {
|
const uint8_t header[] = {
|
||||||
'B', 'M',
|
'B', 'M',
|
||||||
(uint8_t)(size >> 0), (uint8_t)(size >> 8), (uint8_t)(size >> 16), (uint8_t)(size >> 24),
|
(uint8_t)(size >> 0), (uint8_t)(size >> 8), (uint8_t)(size >> 16), (uint8_t)(size >> 24),
|
||||||
@ -123,7 +123,7 @@ static void dump_content(FILE *file, const void *frame,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bgr24) // BGR24 byte order. Can directly copy.
|
if (bgr24) /* BGR24 byte order. Can directly copy. */
|
||||||
{
|
{
|
||||||
for (j = 0; j < height; j++, u.u8 += pitch)
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
dump_line_bgr(lines[j], u.u8, width);
|
dump_line_bgr(lines[j], u.u8, width);
|
||||||
@ -133,7 +133,7 @@ static void dump_content(FILE *file, const void *frame,
|
|||||||
for (j = 0; j < height; j++, u.u8 += pitch)
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
dump_line_32(lines[j], u.u32, width);
|
dump_line_32(lines[j], u.u32, width);
|
||||||
}
|
}
|
||||||
else // RGB565
|
else /* RGB565 */
|
||||||
{
|
{
|
||||||
for (j = 0; j < height; j++, u.u8 += pitch)
|
for (j = 0; j < height; j++, u.u8 += pitch)
|
||||||
dump_line_16(lines[j], u.u16, width);
|
dump_line_16(lines[j], u.u16, width);
|
||||||
@ -148,7 +148,7 @@ end:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Take frame bottom-up.
|
/* Take frame bottom-up. */
|
||||||
bool screenshot_dump(const char *folder, const void *frame,
|
bool screenshot_dump(const char *folder, const void *frame,
|
||||||
unsigned width, unsigned height, int pitch, bool bgr24)
|
unsigned width, unsigned height, int pitch, bool bgr24)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user