diff mbox series

[FFmpeg-devel,v6] avformat/mov: Memory optimization with QuickTime/MP4

Message ID d28b1f22780c4a14ade181b9ac469b19@scisys.com
State New
Headers show
Series [FFmpeg-devel,v6] avformat/mov: Memory optimization with QuickTime/MP4 | expand

Checks

Context Check Description
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Jörg Beckmann Jan. 8, 2020, 1:26 p.m. UTC
Invents a new option "discard_fragments" for the MP4/Quicktime/MOV decoder.

If this option is set to "on", old fragments are discarded as far as possible
on each call to switch_root(). If set to "off", nothing changes at all. If set
to "auto" (the default), this function is turned on for streams containing
only audio.

For pure audio streams, the memory usage is now constant. For video streams,
the memory usage is reduced. It's tested with audio streams received from a
professional DAB+ receiver and with video streams created on my own with
"ffmpeg -i <video>.m4v -c:a:0 copy -c:v copy -c:s copy -f ismv -movflags \
frag_keyframe -movflags faststart tcp://localhost:1234?listen" and
"ffmpeg -i tcp://localhost:1234 -c:a copy -c:v copy -c:s copy -y <output>".

For pure audio streams, this function is intensively tested and in production
use for several weeks. For video streams, it is tested with some examples, but is
more or less experimental. Therefore "auto" turns it on for pure audio streams
only.

Signed-off-by: Jörg Beckmann mailto:joerg.beckmann@scisys.com
---
 libavformat/isom.h |  1 +
 libavformat/mov.c  | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 59 insertions(+), 2 deletions(-)

Comments

Moritz Barsnick Jan. 8, 2020, 2:25 p.m. UTC | #1
On Wed, Jan 08, 2020 at 13:26:35 +0000, Jörg Beckmann wrote:
> Invents a new option "discard_fragments" for the MP4/Quicktime/MOV decoder.

Strictly speaking, it's a demuxer and not a decoder. ;-)

> If this option is set to "on", old fragments are discarded as far as possible
> on each call to switch_root(). If set to "off", nothing changes at all. If set
> to "auto" (the default), this function is turned on for streams containing
> only audio.

Since it's new option, and possibly even a behavioral change, I suggest
bumping libavformat's MICRO version with the same commit.

> +    { "discard_fragments", "Discard fragments after they have been read to support live streams.", OFFSET(discard_fragments),
> +        AV_OPT_TYPE_INT, { .i64 = MOV_DISCARD_AUTO }, MOV_DISCARD_AUTO, MOV_DISCARD_ON, FLAGS, "discard_fragments"},
> +        { "auto", "Switch on for audio only streams", 0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_AUTO}, INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
> +        { "off",  "Switch off",                       0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_OFF},  INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
> +        { "on",   "Switch on",                        0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_ON},   INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
>      { NULL },

I would have suggest you add documentation to doc/demuxers.texi, but
since most of mov's options don't seem to be documented there...
*sigh*.

Moritz
Jörg Beckmann Jan. 17, 2020, 8:49 a.m. UTC | #2
> -----Ursprüngliche Nachricht-----
> Von: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> Im Auftrag von Moritz
> Barsnick
> Gesendet: Mittwoch, 8. Januar 2020 15:25
> An: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
> Betreff: [SCISYS Possible Spam] Re: [FFmpeg-devel] [PATCH v6] avformat/mov:
> Memory optimization with QuickTime/MP4
> 
> On Wed, Jan 08, 2020 at 13:26:35 +0000, Jörg Beckmann wrote:
> > Invents a new option "discard_fragments" for the MP4/Quicktime/MOV decoder.
> 
> Strictly speaking, it's a demuxer and not a decoder. ;-)
> 
> > If this option is set to "on", old fragments are discarded as far as
> > possible on each call to switch_root(). If set to "off", nothing
> > changes at all. If set to "auto" (the default), this function is
> > turned on for streams containing only audio.
> 
> Since it's new option, and possibly even a behavioral change, I suggest bumping
> libavformat's MICRO version with the same commit.

Is this something I must do? 

> > +    { "discard_fragments", "Discard fragments after they have been read to
> support live streams.", OFFSET(discard_fragments),
> > +        AV_OPT_TYPE_INT, { .i64 = MOV_DISCARD_AUTO },
> MOV_DISCARD_AUTO, MOV_DISCARD_ON, FLAGS, "discard_fragments"},
> > +        { "auto", "Switch on for audio only streams", 0, AV_OPT_TYPE_CONST,
> {.i64 = MOV_DISCARD_AUTO}, INT_MIN, INT_MAX, FLAGS, "discard_fragments"
> },
> > +        { "off",  "Switch off",                       0, AV_OPT_TYPE_CONST, {.i64 =
> MOV_DISCARD_OFF},  INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
> > +        { "on",   "Switch on",                        0, AV_OPT_TYPE_CONST, {.i64 =
> MOV_DISCARD_ON},   INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
> >      { NULL },
> 
> I would have suggest you add documentation to doc/demuxers.texi, but since
> most of mov's options don't seem to be documented there...
> *sigh*.
> 
> Moritz
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with
> subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavformat/isom.h b/libavformat/isom.h
index 4943b80ccf..9b4753f4d7 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -268,6 +268,7 @@  typedef struct MOVContext {
     int advanced_editlist;
     int ignore_chapters;
     int seek_individually;
+    int discard_fragments;
     int64_t next_root_atom; ///< offset of the next root atom
     int export_all;
     int export_xmp;
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 890c6e85b8..a3289dda20 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -66,6 +66,10 @@ 
 
 #include "qtpalette.h"
 
+#define MOV_DISCARD_AUTO (-1)
+#define MOV_DISCARD_OFF   0
+#define MOV_DISCARD_ON    1
+
 /* those functions parse an atom */
 /* links atom IDs to parse functions */
 typedef struct MOVParseTableEntry {
@@ -7454,6 +7458,7 @@  static int mov_read_header(AVFormatContext *s)
     int j, err;
     MOVAtom atom = { AV_RL32("root") };
     int i;
+    int audio_only = 1;
 
     if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
         av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
@@ -7522,6 +7527,9 @@  static int mov_read_header(AVFormatContext *s)
         AVStream *st = s->streams[i];
         MOVStreamContext *sc = st->priv_data;
         fix_timescale(mov, sc);
+        if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
+            audio_only = 0;
+        }
         if(st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AAC) {
             st->skip_samples = sc->start_pad;
         }
@@ -7547,6 +7555,10 @@  static int mov_read_header(AVFormatContext *s)
         }
     }
 
+    if (mov->discard_fragments == MOV_DISCARD_AUTO) {
+        mov->discard_fragments = audio_only;
+    }
+
     if (mov->trex_data) {
         for (i = 0; i < s->nb_streams; i++) {
             AVStream *st = s->streams[i];
@@ -7692,8 +7704,11 @@  static int should_retry(AVIOContext *pb, int error_code) {
 
 static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
 {
-    int ret;
+    int ret, i;
     MOVContext *mov = s->priv_data;
+    AVStream *st = NULL;
+    MOVStreamContext *sc;
+    MOVFragment *frag;
 
     if (index >= 0 && index < mov->frag_index.nb_items)
         target = mov->frag_index.item[index].moof_offset;
@@ -7715,6 +7730,43 @@  static int mov_switch_root(AVFormatContext *s, int64_t target, int index)
 
     mov->found_mdat = 0;
 
+    if (mov->discard_fragments) {
+        frag = &mov->fragment;
+
+        for (i = 0; i < mov->fc->nb_streams; i++) {
+            if (mov->fc->streams[i]->id == frag->track_id) {
+                st = mov->fc->streams[i];
+                break;
+            }
+        }
+
+        av_assert0(st);
+
+        sc = st->priv_data;
+
+        switch (st->codecpar->codec_type) {
+            case AVMEDIA_TYPE_AUDIO:
+            case AVMEDIA_TYPE_SUBTITLE:
+                /* Freeing VIDEO tables leads to corrupted video when writing to eg. MKV */
+                av_freep(&st->index_entries);
+                st->nb_index_entries = 0;
+                st->index_entries_allocated_size = 0;
+
+                sc->current_index = 0;
+                sc->current_sample = 0;
+
+                av_freep(&sc->ctts_data);
+                sc->ctts_allocated_size = 0;
+                sc->ctts_count = 0;
+                break;
+        }
+
+        av_free(mov->frag_index.item->stream_info);
+        av_freep(&mov->frag_index.item);
+        mov->frag_index.allocated_size = 0;
+        mov->frag_index.nb_items = 0;
+    }
+
     ret = mov_read_default(mov, s->pb, (MOVAtom){ AV_RL32("root"), INT64_MAX });
     if (ret < 0)
         return ret;
@@ -8057,7 +8109,11 @@  static const AVOption mov_options[] = {
     { "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
     { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL,
         {.i64 = 0}, 0, 1, FLAGS },
-
+    { "discard_fragments", "Discard fragments after they have been read to support live streams.", OFFSET(discard_fragments),
+        AV_OPT_TYPE_INT, { .i64 = MOV_DISCARD_AUTO }, MOV_DISCARD_AUTO, MOV_DISCARD_ON, FLAGS, "discard_fragments"},
+        { "auto", "Switch on for audio only streams", 0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_AUTO}, INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
+        { "off",  "Switch off",                       0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_OFF},  INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
+        { "on",   "Switch on",                        0, AV_OPT_TYPE_CONST, {.i64 = MOV_DISCARD_ON},   INT_MIN, INT_MAX, FLAGS, "discard_fragments" },
     { NULL },
 };