You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
asrc_flite: do not crash on multiple instances.
The voice register functions return the same voice structure upon multiple registration. It causes us two problems: If we delete a voice without deregistering it, it leaves a dangling pointer inside the library. If we delete or unregister a voice at uninit, it may still be in use by another instance of the filter. The second problem is solved by keeping an usage counter inside asrc_flite. This is not thread-safe, but neither is flite itself.
This commit is contained in:
@@ -1033,6 +1033,8 @@ Synthesize a voice utterance using the libflite library.
|
|||||||
To enable compilation of this filter you need to configure FFmpeg with
|
To enable compilation of this filter you need to configure FFmpeg with
|
||||||
@code{--enable-libflite}.
|
@code{--enable-libflite}.
|
||||||
|
|
||||||
|
Note that the flite library is not thread-safe.
|
||||||
|
|
||||||
The source accepts parameters as a list of @var{key}=@var{value} pairs,
|
The source accepts parameters as a list of @var{key}=@var{value} pairs,
|
||||||
separated by ":".
|
separated by ":".
|
||||||
|
|
||||||
|
@@ -42,6 +42,7 @@ typedef struct {
|
|||||||
int wave_nb_samples;
|
int wave_nb_samples;
|
||||||
int list_voices;
|
int list_voices;
|
||||||
cst_voice *voice;
|
cst_voice *voice;
|
||||||
|
struct voice_entry *voice_entry;
|
||||||
int64_t pts;
|
int64_t pts;
|
||||||
int frame_nb_samples; ///< number of samples per frame
|
int frame_nb_samples; ///< number of samples per frame
|
||||||
} FliteContext;
|
} FliteContext;
|
||||||
@@ -64,7 +65,9 @@ AVFILTER_DEFINE_CLASS(flite);
|
|||||||
static volatile int flite_inited = 0;
|
static volatile int flite_inited = 0;
|
||||||
|
|
||||||
/* declare functions for all the supported voices */
|
/* declare functions for all the supported voices */
|
||||||
#define DECLARE_REGISTER_VOICE_FN(name) cst_voice *register_cmu_us_## name(const char *)
|
#define DECLARE_REGISTER_VOICE_FN(name) \
|
||||||
|
cst_voice *register_cmu_us_## name(const char *); \
|
||||||
|
void unregister_cmu_us_## name(cst_voice *);
|
||||||
DECLARE_REGISTER_VOICE_FN(awb);
|
DECLARE_REGISTER_VOICE_FN(awb);
|
||||||
DECLARE_REGISTER_VOICE_FN(kal);
|
DECLARE_REGISTER_VOICE_FN(kal);
|
||||||
DECLARE_REGISTER_VOICE_FN(kal16);
|
DECLARE_REGISTER_VOICE_FN(kal16);
|
||||||
@@ -74,14 +77,22 @@ DECLARE_REGISTER_VOICE_FN(slt);
|
|||||||
struct voice_entry {
|
struct voice_entry {
|
||||||
const char *name;
|
const char *name;
|
||||||
cst_voice * (*register_fn)(const char *);
|
cst_voice * (*register_fn)(const char *);
|
||||||
|
void (*unregister_fn)(cst_voice *);
|
||||||
|
cst_voice *voice;
|
||||||
|
unsigned usage_count;
|
||||||
} voice_entry;
|
} voice_entry;
|
||||||
|
|
||||||
|
#define MAKE_VOICE_STRUCTURE(voice_name) { \
|
||||||
|
.name = #voice_name, \
|
||||||
|
.register_fn = register_cmu_us_ ## voice_name, \
|
||||||
|
.unregister_fn = unregister_cmu_us_ ## voice_name, \
|
||||||
|
}
|
||||||
static struct voice_entry voice_entries[] = {
|
static struct voice_entry voice_entries[] = {
|
||||||
{ "awb", register_cmu_us_awb },
|
MAKE_VOICE_STRUCTURE(awb),
|
||||||
{ "kal", register_cmu_us_kal },
|
MAKE_VOICE_STRUCTURE(kal),
|
||||||
{ "kal16", register_cmu_us_kal16 },
|
MAKE_VOICE_STRUCTURE(kal16),
|
||||||
{ "rms", register_cmu_us_rms },
|
MAKE_VOICE_STRUCTURE(rms),
|
||||||
{ "slt", register_cmu_us_slt },
|
MAKE_VOICE_STRUCTURE(slt),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void list_voices(void *log_ctx, const char *sep)
|
static void list_voices(void *log_ctx, const char *sep)
|
||||||
@@ -92,19 +103,22 @@ static void list_voices(void *log_ctx, const char *sep)
|
|||||||
voice_entries[i].name, i < (n-1) ? sep : "\n");
|
voice_entries[i].name, i < (n-1) ? sep : "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int select_voice(cst_voice **voice, const char *voice_name, void *log_ctx)
|
static int select_voice(struct voice_entry **entry_ret, const char *voice_name, void *log_ctx)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < FF_ARRAY_ELEMS(voice_entries); i++) {
|
for (i = 0; i < FF_ARRAY_ELEMS(voice_entries); i++) {
|
||||||
struct voice_entry *entry = &voice_entries[i];
|
struct voice_entry *entry = &voice_entries[i];
|
||||||
if (!strcmp(entry->name, voice_name)) {
|
if (!strcmp(entry->name, voice_name)) {
|
||||||
*voice = entry->register_fn(NULL);
|
if (!entry->voice)
|
||||||
if (!*voice) {
|
entry->voice = entry->register_fn(NULL);
|
||||||
|
if (!entry->voice) {
|
||||||
av_log(log_ctx, AV_LOG_ERROR,
|
av_log(log_ctx, AV_LOG_ERROR,
|
||||||
"Could not register voice '%s'\n", voice_name);
|
"Could not register voice '%s'\n", voice_name);
|
||||||
return AVERROR_UNKNOWN;
|
return AVERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
entry->usage_count++;
|
||||||
|
*entry_ret = entry;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,8 +156,9 @@ static av_cold int init(AVFilterContext *ctx, const char *args)
|
|||||||
flite_inited++;
|
flite_inited++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = select_voice(&flite->voice, flite->voice_str, ctx)) < 0)
|
if ((ret = select_voice(&flite->voice_entry, flite->voice_str, ctx)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
flite->voice = flite->voice_entry->voice;
|
||||||
|
|
||||||
if (flite->textfile && flite->text) {
|
if (flite->textfile && flite->text) {
|
||||||
av_log(ctx, AV_LOG_ERROR,
|
av_log(ctx, AV_LOG_ERROR,
|
||||||
@@ -188,8 +203,10 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||||||
|
|
||||||
av_opt_free(flite);
|
av_opt_free(flite);
|
||||||
|
|
||||||
delete_voice(flite->voice);
|
if (!--flite->voice_entry->usage_count)
|
||||||
|
flite->voice_entry->unregister_fn(flite->voice);
|
||||||
flite->voice = NULL;
|
flite->voice = NULL;
|
||||||
|
flite->voice_entry = NULL;
|
||||||
delete_wave(flite->wave);
|
delete_wave(flite->wave);
|
||||||
flite->wave = NULL;
|
flite->wave = NULL;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user