[FFmpeg-devel] avformat/avio: don't download data twice

Submitted by Adrian Trzciński on Dec. 6, 2018, 12:28 p.m.

Details

Message ID CAEb68tMx9tgTQX38e=f-yK7i7BFhw4vRyzb-qJfvoZoPB3xgSA@mail.gmail.com
State New
Headers show

Commit Message

Adrian Trzciński Dec. 6, 2018, 12:28 p.m.
Like I wrote in #7592 in case of data muxed in a way, that video and audio
packets for the same ptses are more than 1 sec distant (so when
ff_configure_buffers_for_index changes size of AVIOContext buffers to 2 *
biggest_distance_between_data_for_1_sec_pts) FFmpeg fetches the same data
twice from the server. This is because in fill_buffer in aviobuf.c when we
need data audio from pts x that we don't currently have in buffer, we drop
the whole buffer and download new range. But afterward, we need video from
pts x, which was in buffer already (before seek caused by audio), so we
drop buffer again and seek to this position.

The patch changes the mechanism of data reading in aviobuf.c/fill_buffer.
If data aren't in buffer new mechanism still leaves one time_unit of data
in a buffer for further seeks to the same pts but in a different stream (it
needs to be within one time_unit, it is calculated in
ff_configure_buffers_for_index).

Patch hide | download patch | download mbox

From 71cadd60a2f71d2d39bd7999e080c206291b55da Mon Sep 17 00:00:00 2001
From: Adrian Trzcinski <atrzcinski@vewd.com>
Date: Tue, 4 Dec 2018 19:00:22 +0100
Subject: [PATCH] avformat/avio: fill_buffer drops less data

Signed-off-by: Adrian Trzcinski <atrzcinski@vewd.com>
---
 libavformat/avio.h    |  1 +
 libavformat/aviobuf.c | 21 ++++++++++++++++-----
 libavformat/utils.c   |  1 +
 3 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/libavformat/avio.h b/libavformat/avio.h
index 75912ce6be..c44e1c7e9c 100644
--- a/libavformat/avio.h
+++ b/libavformat/avio.h
@@ -225,6 +225,7 @@  typedef struct AVIOContext {
      */
     unsigned char *buffer;  /**< Start of the buffer. */
     int buffer_size;        /**< Maximum buffer size */
+    int time_units_in_buffer; /**< Number of time units in buffer */
     unsigned char *buf_ptr; /**< Current position in the buffer */
     unsigned char *buf_end; /**< End of the data, may be less than
                                  buffer+buffer_size if the read function returned
diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 5a33f82950..afccc61491 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -92,6 +92,7 @@  int ffio_init_context(AVIOContext *s,
     s->buffer      = buffer;
     s->orig_buffer_size =
     s->buffer_size = buffer_size;
+    s->time_units_in_buffer = 0;
     s->buf_ptr     = buffer;
     s->buf_ptr_max = buffer;
     s->opaque      = opaque;
@@ -550,9 +551,19 @@  static void fill_buffer(AVIOContext *s)
 {
     int max_buffer_size = s->max_packet_size ?
                           s->max_packet_size : IO_BUFFER_SIZE;
-    uint8_t *dst        = s->buf_end - s->buffer + max_buffer_size < s->buffer_size ?
-                          s->buf_end : s->buffer;
-    int len             = s->buffer_size - (dst - s->buffer);
+    int need_erase = !(s->buf_end - s->buffer + max_buffer_size < s->buffer_size);
+    uint8_t *dst = need_erase ? s->buffer : s->buf_end;
+    int len;
+
+    if (need_erase && s->time_units_in_buffer > 1) {
+        const int bytes_in_time_unit = (s->buf_end - s->buffer) / s->time_units_in_buffer;
+        const int offset = bytes_in_time_unit * (s->time_units_in_buffer - 1);
+        memcpy(s->buffer, s->buffer + offset, bytes_in_time_unit);
+        dst = s->buf_end = s->buffer + bytes_in_time_unit;
+        s->buf_ptr = s->buf_ptr - s->buffer < offset ? s->buffer : s->buf_ptr - offset;
+    }
+
+    len = s->buffer_size - (dst - s->buffer);
 
     /* can't fill the buffer without read_packet, just set EOF if appropriate */
     if (!s->read_packet && s->buf_ptr >= s->buf_end)
@@ -562,7 +573,7 @@  static void fill_buffer(AVIOContext *s)
     if (s->eof_reached)
         return;
 
-    if (s->update_checksum && dst == s->buffer) {
+    if (s->update_checksum && need_erase) {
         if (s->buf_end > s->checksum_ptr)
             s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
                                              s->buf_end - s->checksum_ptr);
@@ -571,7 +582,7 @@  static void fill_buffer(AVIOContext *s)
 
     /* make buffer smaller in case it ended up large after probing */
     if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size) {
-        if (dst == s->buffer && s->buf_ptr != dst) {
+        if (need_erase  && s->buf_ptr != dst) {
             int ret = ffio_set_buf_size(s, s->orig_buffer_size);
             if (ret < 0)
                 av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 93e588ee1e..62deaa710b 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -2138,6 +2138,7 @@  void ff_configure_buffers_for_index(AVFormatContext *s, int64_t time_tolerance)
         av_log(s, AV_LOG_VERBOSE, "Reconfiguring buffers to size %"PRId64"\n", pos_delta);
         ffio_set_buf_size(s->pb, pos_delta);
         s->pb->short_seek_threshold = FFMAX(s->pb->short_seek_threshold, pos_delta/2);
+        s->pb->time_units_in_buffer = 2;
     }
 
     if (skip < (1<<23)) {
-- 
2.19.2