diff --git a/doc/filters.texi b/doc/filters.texi
index d19fd346ae..3a127369ea 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -9659,13 +9659,13 @@ The default value of @var{borderw} is 0.
 Set the color to be used for drawing border around text. For the syntax of this
 option, check the @ref{color syntax,,"Color" section in the ffmpeg-utils manual,ffmpeg-utils}.
 
 The default value of @var{bordercolor} is "black".
 
 @item expansion
-Select how the @var{text} is expanded. Can be either @code{none},
+Select how the @var{text} and @var{textfile} are expanded. Can be either @code{none},
 @code{strftime} (deprecated) or
 @code{normal} (default). See the @ref{drawtext_expansion, Text expansion} section
 below for details.
 
 @item basetime
 Set a start time for the count. Value is in microseconds. Only applied
@@ -9786,15 +9786,19 @@ of UTF-8 encoded characters.
 
 This parameter is mandatory if no text string is specified with the
 parameter @var{text}.
 
 If both @var{text} and @var{textfile} are specified, an error is thrown.
 
+This parameter supports (per frame) variable expansion. Per frame variable 
+expansion requires @code{reload=1}. See @var{expansion} for details.
+
+
 @item reload
-If set to 1, the @var{textfile} will be reloaded before each frame.
-Be sure to update it atomically, or it may be read partially, or even fail.
+If set to 1, then before each frame, @var{textfile} file path will be expanded and then the file at said path will be reloaded.
+Be sure to update the file atomically, or it may be read partially, or even fail.
 
 @item x
 @item y
 The expressions which specify the offsets where text will be drawn
 within the video frame. They are relative to the top/left border of the
 output image.
@@ -10060,12 +10064,18 @@ drawtext="fontsize=15:fontfile=FreeSerif.ttf:text=LONG_LINE:y=h-line_h:x=-50*t"
 @item
 Show the content of file @file{CREDITS} off the bottom of the frame and scroll up.
 @example
 drawtext="fontsize=20:fontfile=FreeSerif.ttf:textfile=CREDITS:y=h-20*t"
 @end example
 
+@item
+Each frame, reload a different text file at /path/to/frameX.txt, where X is replaced with the frame number being processed by the filter
+@example
+drawtext="expansion:normal:textfile=/path/to/frame%{frame_num}.txt:reload=1"
+@end example
+
 @item
 Draw a single green letter "g", at the center of the input video.
 The glyph baseline is placed at half screen height.
 @example
 drawtext="fontsize=60:fontfile=FreeSerif.ttf:fontcolor=green:text=g:x=(w-max_glyph_w)/2:y=h/2-ascent"
 @end example
diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c
index abe1ca6c35..ffb1ff2330 100644
--- a/libavfilter/vf_drawtext.c
+++ b/libavfilter/vf_drawtext.c
@@ -152,13 +152,14 @@ typedef struct DrawTextContext {
     AVBPrint expanded_text;         ///< used to contain the expanded text
     uint8_t *fontcolor_expr;        ///< fontcolor expression to evaluate
     AVBPrint expanded_fontcolor;    ///< used to contain the expanded fontcolor spec
     int ft_load_flags;              ///< flags used for loading fonts, see FT_LOAD_*
     FT_Vector *positions;           ///< positions for each element in the text
     size_t nb_positions;            ///< number of elements of positions array
-    char *textfile;                 ///< file with text to be drawn
+    char *textfile;                 ///< filename with text to be drawn
+    AVBPrint expanded_textfile;     ///< Same as textfile, except the filename can be expanded
     int x;                          ///< x position to start drawing text
     int y;                          ///< y position to start drawing text
     int max_glyph_w;                ///< max glyph width
     int max_glyph_h;                ///< max glyph height
     int shadowx, shadowy;
     int borderw;                    ///< border width
@@ -565,24 +566,33 @@ static int load_font(AVFilterContext *ctx)
     if (!err)
         return 0;
 #endif
     return err;
 }
 
+static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp);
 static int load_textfile(AVFilterContext *ctx)
 {
     DrawTextContext *s = ctx->priv;
     int err;
     uint8_t *textbuf;
     uint8_t *tmp;
     size_t textbuf_size;
 
-    if ((err = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
+    if ((err = expand_text(ctx, s->textfile, &s->expanded_textfile)) < 0) {
+        av_log(ctx, AV_LOG_ERROR, "The text file path '%s' is not expandable\n",
+            s->textfile);
+        return err;
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "expanded_textfile:%s\n", s->expanded_textfile.str);
+
+    if ((err = av_file_map(s->expanded_textfile.str, &textbuf, &textbuf_size, 0, ctx)) < 0) {
         av_log(ctx, AV_LOG_ERROR,
                "The text file '%s' could not be read or is empty\n",
-               s->textfile);
+               s->expanded_textfile.str);
         return err;
     }
 
     if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(s->text, textbuf_size + 1))) {
         av_file_unmap(textbuf, textbuf_size);
         return AVERROR(ENOMEM);
@@ -702,12 +712,14 @@ static av_cold int init(AVFilterContext *ctx)
 
     if (!s->fontfile && !CONFIG_LIBFONTCONFIG) {
         av_log(ctx, AV_LOG_ERROR, "No font filename provided\n");
         return AVERROR(EINVAL);
     }
 
+    av_bprint_init(&s->expanded_textfile, 0, AV_BPRINT_SIZE_UNLIMITED);
+
     if (s->textfile) {
         if (s->text) {
             av_log(ctx, AV_LOG_ERROR,
                    "Both text and text file provided. Please provide only one\n");
             return AVERROR(EINVAL);
         }
@@ -820,12 +832,13 @@ static av_cold void uninit(AVFilterContext *ctx)
     FT_Done_Face(s->face);
     FT_Stroker_Done(s->stroker);
     FT_Done_FreeType(s->library);
 
     av_bprint_finalize(&s->expanded_text, NULL);
     av_bprint_finalize(&s->expanded_fontcolor, NULL);
+    av_bprint_finalize(&s->expanded_textfile, NULL);
 }
 
 static int config_input(AVFilterLink *inlink)
 {
     AVFilterContext *ctx = inlink->dst;
     DrawTextContext *s = ctx->priv;
