[FFmpeg-devel] [PATCH 1/5] avformat/fifo: always wait recovery_wait_time between recoveries

Jan Ekström jeebjp at gmail.com
Mon Dec 7 12:08:41 EET 2020


From: Bernard Boulay <bernard.boulay at 24i.com>

With various formats/protocols - for example fragmented mp4 over
HTTP - success/failure might not appear right away. The header and
first packet might get successfully written into the muxer, but
a response of failure might appear only N packets later.

In such a case, a recovery attempt would always be the "first time",
and it would thus be executed without delay, causing effectively a
DoS towards the server on the other side of the connection.

Thus, modify the logic of the module so that it takes into account
the last time recovery was attempted - even if it seemed to be
successful:
1. Refactor update of last_recovery_ts to its own helper function.
2. Call this function even if the recovery is successful, thus
   ensuring that the time is always set.
3. Move initialization of last_recovery_ts to be handled together
   with the thread initialization.
4. Do not limit sleeping in recovery based on recovery_nr being
   nonzero ("not the first time of attempted recovery").

Signed-off-by: Jan Ekström <jan.ekstrom at 24i.com>
---
 libavformat/fifo.c | 54 +++++++++++++++++++++++++---------------------
 1 file changed, 30 insertions(+), 24 deletions(-)

diff --git a/libavformat/fifo.c b/libavformat/fifo.c
index 17748e94ce5..55cbf14ae93 100644
--- a/libavformat/fifo.c
+++ b/libavformat/fifo.c
@@ -279,15 +279,10 @@ static void free_message(void *msg)
         av_packet_unref(&fifo_msg->pkt);
 }
 
-static int fifo_thread_process_recovery_failure(FifoThreadContext *ctx, AVPacket *pkt,
-                                                int err_no)
+static void fifo_thread_set_last_recovery_ts(FifoThreadContext *ctx, AVPacket *pkt)
 {
     AVFormatContext *avf = ctx->avf;
     FifoContext *fifo = avf->priv_data;
-    int ret;
-
-    av_log(avf, AV_LOG_INFO, "Recovery failed: %s\n",
-           av_err2str(err_no));
 
     if (fifo->recovery_wait_streamtime) {
         if (pkt->pts == AV_NOPTS_VALUE)
@@ -297,6 +292,19 @@ static int fifo_thread_process_recovery_failure(FifoThreadContext *ctx, AVPacket
     } else {
         ctx->last_recovery_ts = av_gettime_relative();
     }
+}
+
+static int fifo_thread_process_recovery_failure(FifoThreadContext *ctx, AVPacket *pkt,
+                                                int err_no)
+{
+    AVFormatContext *avf = ctx->avf;
+    FifoContext *fifo = avf->priv_data;
+    int ret;
+
+    av_log(avf, AV_LOG_INFO, "Recovery failed: %s\n",
+           av_err2str(err_no));
+
+    fifo_thread_set_last_recovery_ts(ctx, pkt);
 
     if (fifo->max_recovery_attempts &&
         ctx->recovery_nr >= fifo->max_recovery_attempts) {
@@ -329,27 +337,22 @@ static int fifo_thread_attempt_recovery(FifoThreadContext *ctx, FifoMessage *msg
         ctx->header_written = 0;
     }
 
-    if (!ctx->recovery_nr) {
-        ctx->last_recovery_ts = fifo->recovery_wait_streamtime ?
-                                AV_NOPTS_VALUE : 0;
-    } else {
-        if (fifo->recovery_wait_streamtime) {
-            if (ctx->last_recovery_ts == AV_NOPTS_VALUE) {
-                AVRational tb = avf->streams[pkt->stream_index]->time_base;
-                time_since_recovery = av_rescale_q(pkt->pts - ctx->last_recovery_ts,
-                                                   tb, AV_TIME_BASE_Q);
-            } else {
-                /* Enforce recovery immediately */
-                time_since_recovery = fifo->recovery_wait_time;
-            }
+    if (fifo->recovery_wait_streamtime) {
+        if (ctx->last_recovery_ts == AV_NOPTS_VALUE) {
+            AVRational tb = avf->streams[pkt->stream_index]->time_base;
+            time_since_recovery = av_rescale_q(pkt->pts - ctx->last_recovery_ts,
+                                               tb, AV_TIME_BASE_Q);
         } else {
-            time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
+            /* Enforce recovery immediately */
+            time_since_recovery = fifo->recovery_wait_time;
         }
-
-        if (time_since_recovery < fifo->recovery_wait_time)
-            return AVERROR(EAGAIN);
+    } else {
+        time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
     }
 
+    if (time_since_recovery < fifo->recovery_wait_time)
+        return AVERROR(EAGAIN);
+
     ctx->recovery_nr++;
 
     if (fifo->max_recovery_attempts) {
@@ -372,6 +375,7 @@ static int fifo_thread_attempt_recovery(FifoThreadContext *ctx, FifoMessage *msg
         }
     } else {
         av_log(avf, AV_LOG_INFO, "Recovery successful\n");
+        fifo_thread_set_last_recovery_ts(ctx, pkt);
         ctx->recovery_nr = 0;
     }
 
@@ -389,7 +393,7 @@ static int fifo_thread_recover(FifoThreadContext *ctx, FifoMessage *msg, int err
     int ret;
 
     do {
-        if (!fifo->recovery_wait_streamtime && ctx->recovery_nr > 0) {
+        if (!fifo->recovery_wait_streamtime) {
             int64_t time_since_recovery = av_gettime_relative() - ctx->last_recovery_ts;
             int64_t time_to_wait = FFMAX(0, fifo->recovery_wait_time - time_since_recovery);
             if (time_to_wait)
@@ -420,6 +424,8 @@ static void *fifo_consumer_thread(void *data)
     memset(&fifo_thread_ctx, 0, sizeof(FifoThreadContext));
     fifo_thread_ctx.avf = avf;
     fifo_thread_ctx.last_received_dts = AV_NOPTS_VALUE;
+    fifo_thread_ctx.last_recovery_ts = fifo->recovery_wait_streamtime ?
+                                       AV_NOPTS_VALUE : 0;
 
     while (1) {
         uint8_t just_flushed = 0;
-- 
2.29.2



More information about the ffmpeg-devel mailing list