diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 1f11a2f002..cb4d90c7b2 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -42,6 +42,7 @@
 #include "libavutil/eval.h"
 #include "libavutil/fifo.h"
 #include "libavutil/hwcontext.h"
+#include "libavutil/parseutils.h"
 #include "libavutil/pixfmt.h"
 #include "libavutil/rational.h"
 #include "libavutil/thread.h"
@@ -437,36 +438,8 @@ enum forced_keyframes_const {
 #define ABORT_ON_FLAG_EMPTY_OUTPUT        (1 <<  0)
 #define ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM (1 <<  1)
 
-enum EncStatsType {
-    ENC_STATS_LITERAL = 0,
-    ENC_STATS_FILE_IDX,
-    ENC_STATS_STREAM_IDX,
-    ENC_STATS_FRAME_NUM,
-    ENC_STATS_FRAME_NUM_IN,
-    ENC_STATS_TIMEBASE,
-    ENC_STATS_TIMEBASE_IN,
-    ENC_STATS_PTS,
-    ENC_STATS_PTS_TIME,
-    ENC_STATS_PTS_IN,
-    ENC_STATS_PTS_TIME_IN,
-    ENC_STATS_DTS,
-    ENC_STATS_DTS_TIME,
-    ENC_STATS_SAMPLE_NUM,
-    ENC_STATS_NB_SAMPLES,
-    ENC_STATS_PKT_SIZE,
-    ENC_STATS_BITRATE,
-    ENC_STATS_AVG_BITRATE,
-};
-
-typedef struct EncStatsComponent {
-    enum EncStatsType type;
-
-    uint8_t *str;
-    size_t   str_len;
-} EncStatsComponent;
-
 typedef struct EncStats {
-    EncStatsComponent  *components;
+    AVEncStatsComponent  *components;
     int              nb_components;
 
     AVIOContext        *io;
diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index fa4539664f..a499bc0c81 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -30,6 +30,7 @@
 #include "libavutil/frame.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/log.h"
+#include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/rational.h"
 #include "libavutil/timestamp.h"
@@ -499,7 +500,7 @@ void enc_stats_write(OutputStream *ost, EncStats *es,
     }
 
     for (size_t i = 0; i < es->nb_components; i++) {
-        const EncStatsComponent *c = &es->components[i];
+        const AVEncStatsComponent *c = &es->components[i];
 
         switch (c->type) {
         case ENC_STATS_LITERAL:         avio_write (io, c->str,     c->str_len);                    continue;
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 6c473a8f09..6acdf92c2c 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -242,152 +242,26 @@ void of_enc_stats_close(void)
     nb_enc_stats_files = 0;
 }
 
-static int unescape(char **pdst, size_t *dst_len,
-                    const char **pstr, char delim)
-{
-    const char *str = *pstr;
-    char *dst;
-    size_t len, idx;
-
-    *pdst = NULL;
-
-    len = strlen(str);
-    if (!len)
-        return 0;
-
-    dst = av_malloc(len + 1);
-    if (!dst)
-        return AVERROR(ENOMEM);
-
-    for (idx = 0; *str; idx++, str++) {
-        if (str[0] == '\\' && str[1])
-            str++;
-        else if (*str == delim)
-            break;
-
-        dst[idx] = *str;
-    }
-    if (!idx) {
-        av_freep(&dst);
-        return 0;
-    }
-
-    dst[idx] = 0;
-
-    *pdst    = dst;
-    *dst_len = idx;
-    *pstr    = str;
-
-    return 0;
-}
-
 static int enc_stats_init(OutputStream *ost, EncStats *es, int pre,
                           const char *path, const char *fmt_spec)
 {
-    static const struct {
-        enum EncStatsType  type;
-        const char        *str;
-        int                pre_only:1;
-        int                post_only:1;
-        int                need_input_data:1;
-    } fmt_specs[] = {
-        { ENC_STATS_FILE_IDX,       "fidx"                      },
-        { ENC_STATS_STREAM_IDX,     "sidx"                      },
-        { ENC_STATS_FRAME_NUM,      "n"                         },
-        { ENC_STATS_FRAME_NUM_IN,   "ni",       0, 0, 1         },
-        { ENC_STATS_TIMEBASE,       "tb"                        },
-        { ENC_STATS_TIMEBASE_IN,    "tbi",      0, 0, 1         },
-        { ENC_STATS_PTS,            "pts"                       },
-        { ENC_STATS_PTS_TIME,       "t"                         },
-        { ENC_STATS_PTS_IN,         "ptsi",     0, 0, 1         },
-        { ENC_STATS_PTS_TIME_IN,    "ti",       0, 0, 1         },
-        { ENC_STATS_DTS,            "dts",      0, 1            },
-        { ENC_STATS_DTS_TIME,       "dt",       0, 1            },
-        { ENC_STATS_SAMPLE_NUM,     "sn",       1               },
-        { ENC_STATS_NB_SAMPLES,     "samp",     1               },
-        { ENC_STATS_PKT_SIZE,       "size",     0, 1            },
-        { ENC_STATS_BITRATE,        "br",       0, 1            },
-        { ENC_STATS_AVG_BITRATE,    "abr",      0, 1            },
-    };
-    const char *next = fmt_spec;
-
     int ret;
 
-    while (*next) {
-        EncStatsComponent *c;
-        char *val;
-        size_t val_len;
-
-        // get the sequence up until next opening brace
-        ret = unescape(&val, &val_len, &next, '{');
-        if (ret < 0)
-            return ret;
-
-        if (val) {
-            ret = GROW_ARRAY(es->components, es->nb_components);
-            if (ret < 0) {
-                av_freep(&val);
-                return ret;
-            }
-
-            c          = &es->components[es->nb_components - 1];
-            c->type    = ENC_STATS_LITERAL;
-            c->str     = val;
-            c->str_len = val_len;
-        }
-
-        if (!*next)
-            break;
-        next++;
-
-        // get the part inside braces
-        ret = unescape(&val, &val_len, &next, '}');
-        if (ret < 0)
-            return ret;
-
-        if (!val) {
-            av_log(NULL, AV_LOG_ERROR,
-                   "Empty formatting directive in: %s\n", fmt_spec);
-            return AVERROR(EINVAL);
-        }
-
-        if (!*next) {
-            av_log(NULL, AV_LOG_ERROR,
-                   "Missing closing brace in: %s\n", fmt_spec);
-            ret = AVERROR(EINVAL);
-            goto fail;
-        }
-        next++;
-
-        ret = GROW_ARRAY(es->components, es->nb_components);
-        if (ret < 0)
-            goto fail;
-
-        c = &es->components[es->nb_components - 1];
-
-        for (size_t i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) {
-            if (!strcmp(val, fmt_specs[i].str)) {
-                c->type = fmt_specs[i].type;
-                c->str  = val;
-                c->str_len = val_len;
-                break;
-            }
-        }
-
-        if (!c->type) {
-            av_log(NULL, AV_LOG_ERROR, "Invalid format directive: %s\n", val);
-            ret = AVERROR(EINVAL);
-            goto fail;
-        }
-    }
+    ret = av_parse_enc_stats_components(&es->components, &es->nb_components, fmt_spec);
+    if (ret < 0)
+        goto fail;
 
     for (int j = 0; j < es->nb_components; j++) {
-        EncStatsComponent *c = &es->components[j];
+        AVEncStatsComponent *c = &es->components[j];
         char *val = c->str;
 
-        for (size_t i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) {
-            if (!strcmp(val, fmt_specs[i].str)) {
-                if ((pre && fmt_specs[i].post_only) || (!pre && fmt_specs[i].pre_only)) {
+        for (size_t i = 0; i < av_get_nb_stats_fmt_specs(); i++) {
+            AVEncStatsFormatSpecifier f;
+            if (av_get_stats_fmt_spec(&f, i))
+                goto fail;
+
+            if (!strcmp(val, f.str)) {
+                if ((pre && f.post_only) || (!pre && f.pre_only)) {
                     av_log(NULL, AV_LOG_ERROR,
                            "Format directive '%s' may only be used %s-encoding\n",
                            val, pre ? "post" : "pre");
@@ -395,7 +269,7 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre,
                     goto fail;
                 }
 
-                if (fmt_specs[i].need_input_data && !ost->ist) {
+                if (f.need_input_data && !ost->ist) {
                     av_log(ost, AV_LOG_WARNING,
                            "Format directive '%s' is unavailable, because "
                            "this output stream has no associated input stream\n",
@@ -410,7 +284,7 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre,
     ret = enc_stats_get_file(&es->io, path);
 fail:
     for (int j = 0; j < es->nb_components; j++) {
-        EncStatsComponent *c = &es->components[j];
+        AVEncStatsComponent *c = &es->components[j];
         if (c->type != ENC_STATS_LITERAL) {
             av_freep(&c->str);
         }
diff --git a/libavutil/parseutils.c b/libavutil/parseutils.c
index 94e88e0a79..796811aa6e 100644
--- a/libavutil/parseutils.c
+++ b/libavutil/parseutils.c
@@ -788,3 +788,179 @@ int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info
     }
     return 0;
 }
+
+static const AVEncStatsFormatSpecifier fmt_specs[] = {
+    { ENC_STATS_FILE_IDX,       "fidx"                      },
+    { ENC_STATS_STREAM_IDX,     "sidx"                      },
+    { ENC_STATS_FRAME_NUM,      "n"                         },
+    { ENC_STATS_FRAME_NUM_IN,   "ni",       0, 0, 1         },
+    { ENC_STATS_TIMEBASE,       "tb"                        },
+    { ENC_STATS_TIMEBASE_IN,    "tbi",      0, 0, 1         },
+    { ENC_STATS_PTS,            "pts"                       },
+    { ENC_STATS_PTS_TIME,       "t"                         },
+    { ENC_STATS_PTS_IN,         "ptsi",     0, 0, 1         },
+    { ENC_STATS_PTS_TIME_IN,    "ti",       0, 0, 1         },
+    { ENC_STATS_DTS,            "dts",      0, 1            },
+    { ENC_STATS_DTS_TIME,       "dt",       0, 1            },
+    { ENC_STATS_SAMPLE_NUM,     "sn",       1               },
+    { ENC_STATS_NB_SAMPLES,     "samp",     1               },
+    { ENC_STATS_PKT_SIZE,       "size",     0, 1            },
+    { ENC_STATS_BITRATE,        "br",       0, 1            },
+    { ENC_STATS_AVG_BITRATE,    "abr",      0, 1            },
+};
+
+size_t av_get_nb_stats_fmt_specs(void)
+{
+    return sizeof(fmt_specs) / sizeof(*fmt_specs);
+}
+
+int av_get_stats_fmt_spec(AVEncStatsFormatSpecifier *fmt_spec, int idx)
+{
+    if (!fmt_spec || idx >= av_get_nb_stats_fmt_specs())
+        return AVERROR(EINVAL);
+
+    *fmt_spec = fmt_specs[idx];
+    return 0;
+}
+
+static int unescape(char **pdst, size_t *dst_len,
+                    const char **pstr, char delim)
+{
+    const char *str = *pstr;
+    char *dst;
+    size_t len, idx;
+
+    *pdst = NULL;
+
+    len = strlen(str);
+    if (!len)
+        return 0;
+
+    dst = av_malloc(len + 1);
+    if (!dst)
+        return AVERROR(ENOMEM);
+
+    for (idx = 0; *str; idx++, str++) {
+        if (str[0] == '\\' && str[1])
+            str++;
+        else if (*str == delim)
+            break;
+
+        dst[idx] = *str;
+    }
+    if (!idx) {
+        av_freep(&dst);
+        return 0;
+    }
+
+    dst[idx] = 0;
+
+    *pdst    = dst;
+    *dst_len = idx;
+    *pstr    = str;
+
+    return 0;
+}
+
+static int grow_array(void **array, int elem_size, int *size, int new_size)
+{
+    if (new_size >= INT_MAX / elem_size) {
+        av_log(NULL, AV_LOG_ERROR, "Array too big.\n");
+        return AVERROR(ERANGE);
+    }
+    if (*size < new_size) {
+        uint8_t *tmp = av_realloc_array(*array, new_size, elem_size);
+        if (!tmp)
+            return AVERROR(ENOMEM);
+        memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size);
+        *size = new_size;
+        *array = tmp;
+        return 0;
+    }
+    return 0;
+}
+
+#define GROW_ARRAY(array, nb_elems)\
+    grow_array((void**)&array, sizeof(*array), &nb_elems, nb_elems + 1)
+
+int av_parse_enc_stats_components(AVEncStatsComponent **components, int *nb_components, const char *fmt_spec)
+{
+    const char *next = fmt_spec;
+    int ret;
+
+    while (*next) {
+        AVEncStatsComponent *c;
+        char *val;
+        size_t val_len;
+
+        // get the sequence up until next opening brace
+        ret = unescape(&val, &val_len, &next, '{');
+        if (ret < 0)
+            return ret;
+
+        if (val) {
+            ret = GROW_ARRAY(*components, *nb_components);
+            if (ret < 0) {
+                av_freep(&val);
+                return ret;
+            }
+
+            c          = &((*components)[*nb_components - 1]);
+            c->type    = ENC_STATS_LITERAL;
+            c->str     = val;
+            c->str_len = val_len;
+        }
+
+        if (!*next)
+            break;
+        next++;
+
+        // get the part inside braces
+        ret = unescape(&val, &val_len, &next, '}');
+        if (ret < 0)
+            return ret;
+
+        if (!val) {
+            av_log(NULL, AV_LOG_ERROR,
+                   "Empty formatting directive in: %s\n", fmt_spec);
+            return AVERROR(EINVAL);
+        }
+
+        if (!*next) {
+            av_log(NULL, AV_LOG_ERROR,
+                   "Missing closing brace in: %s\n", fmt_spec);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+        next++;
+
+        ret = GROW_ARRAY(*components, *nb_components);
+        if (ret < 0)
+            goto fail;
+
+        c = &(*components)[*nb_components - 1];
+
+        for (int i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) {
+            if (!strcmp(val, fmt_specs[i].str)) {
+                c->type    = fmt_specs[i].type;
+                c->str     = val;
+                c->str_len = val_len;
+                break;
+            }
+        }
+
+        if (!c->type) {
+            av_log(NULL, AV_LOG_ERROR, "Invalid format directive: %s\n", val);
+            ret = AVERROR(EINVAL);
+            goto fail;
+        }
+
+        continue;
+fail:
+        av_freep(&val);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
diff --git a/libavutil/parseutils.h b/libavutil/parseutils.h
index dad5c2775b..d546d77de0 100644
--- a/libavutil/parseutils.h
+++ b/libavutil/parseutils.h
@@ -22,6 +22,7 @@
 #include <time.h>
 
 #include "rational.h"
+#include "mem.h"
 
 /**
  * @file
@@ -194,4 +195,105 @@ char *av_small_strptime(const char *p, const char *fmt, struct tm *dt);
  */
 time_t av_timegm(struct tm *tm);
 
+typedef enum AVEncStatsType {
+    ENC_STATS_LITERAL = 0,
+    ENC_STATS_FILE_IDX,
+    ENC_STATS_STREAM_IDX,
+    ENC_STATS_FRAME_NUM,
+    ENC_STATS_FRAME_NUM_IN,
+    ENC_STATS_TIMEBASE,
+    ENC_STATS_TIMEBASE_IN,
+    ENC_STATS_PTS,
+    ENC_STATS_PTS_TIME,
+    ENC_STATS_PTS_IN,
+    ENC_STATS_PTS_TIME_IN,
+    ENC_STATS_DTS,
+    ENC_STATS_DTS_TIME,
+    ENC_STATS_SAMPLE_NUM,
+    ENC_STATS_NB_SAMPLES,
+    ENC_STATS_PKT_SIZE,
+    ENC_STATS_BITRATE,
+    ENC_STATS_AVG_BITRATE,
+} AVEncStatsType;
+
+/**
+ * Structure describing an encoding stats component.
+ */
+typedef struct AVEncStatsComponent {
+    /**
+     * The type of this component.
+     */
+    AVEncStatsType type;
+    /**
+     * The string representation of this component.
+     */
+    uint8_t *str;
+    /**
+     * The length of the string.
+     */
+    size_t   str_len;
+} AVEncStatsComponent;
+
+/**
+ * Structure describing an encoding stats format specifier.
+ */
+typedef struct AVEncStatsFormatSpecifier {
+    /**
+     * The type of this format specifier.
+     */
+    enum AVEncStatsType  type;
+    /**
+     * The string representation of this format specifier.
+     */
+    const char          *str;
+    /**
+     * Flag if this format specifier is only valid before encoding.
+     */
+    int                  pre_only:1;
+    /**
+     * Flag if this format specifier is only valid after encoding.
+     */
+    int                  post_only:1;
+    /**
+     * Flag if this format specifier requires an associated input
+     * stream for it to be processed.
+     */
+    int                  need_input_data:1;
+} AVEncStatsFormatSpecifier;
+
+/**
+ * Get the number of available enc stats format specifiers.
+ */
+size_t av_get_nb_stats_fmt_specs(void);
+
+/**
+ * Get a copy of an enc stats format specifier.
+ *
+ * @param *fmt_spec Destination for the copy of the format specifier.
+ *                  Has to be previously allocated.
+ *
+ * @param idx       Index to the table of format specifiers.
+ *
+ * @return          Return 0 on success, a negative value corresponding
+ *                  to an AVERROR code otherwise.
+ */
+int av_get_stats_fmt_spec(AVEncStatsFormatSpecifier *fmt_spec, int idx);
+
+/**
+ * Parse an enc stats format string into an array of AVEncStatsComponent.
+ *
+ * @param components Pointer to the array of AVEncStatsComponent to store
+ *                   the parsed elements. The arrary will be reallocated
+ *                   in the process if any elements are found.
+ *
+ * @param nb_components Pointer to the number of components in the array.
+ *
+ * @param fmt_spec   The format string to parse for components.
+ *
+ * @return          Return 0 on success, a negative value corresponding
+ *                  to an AVERROR code otherwise.
+ *
+ */
+int av_parse_enc_stats_components(AVEncStatsComponent **components, int *nb_components, const char *fmt_spec);
+
 #endif /* AVUTIL_PARSEUTILS_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index c5fa7c3692..0684996bf2 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  58
-#define LIBAVUTIL_VERSION_MINOR  32
+#define LIBAVUTIL_VERSION_MINOR  33
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
