Commit 4ce87ecf authored by Nicolas George's avatar Nicolas George

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.
parent fb0688b0
...@@ -1033,6 +1033,8 @@ Synthesize a voice utterance using the libflite library. ...@@ -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 { ...@@ -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); ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment