From patchwork Wed Jun 5 11:47:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Martin_Storsj=C3=B6?= X-Patchwork-Id: 49579 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a59:d792:0:b0:460:55fa:d5ed with SMTP id db18csp323482vqb; Wed, 5 Jun 2024 04:47:15 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCWoD0bbvLkHxkouBvl+imeUMM8ENTwp7Xw3PDPk/Z49XZPRkBkW9458LfwpVNYGv9tON8v/sJnUWGzeWcUxEvPw3w9TtRni5zHAFA== X-Google-Smtp-Source: AGHT+IEi8AYyIiw+BNOetT2gtME72NqRMcyO399iVf9Sgssa6lLDOIG72AkD2LlrqP7U//CU7LXM X-Received: by 2002:a50:ab43:0:b0:578:33f1:54b5 with SMTP id 4fb4d7f45d1cf-57a8bca1923mr1438385a12.26.1717588035391; Wed, 05 Jun 2024 04:47:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1717588035; cv=none; d=google.com; s=arc-20160816; b=0wIUciLDTxa3Oba+hcpUXJWi0OBMStBhChwdfMxXKYtucVBnFRbkbPyuxqAkE9WWnV NgA0dWulEaooz3zJhQT0KlatwEvXD7Cz75h+HbiS3F23bmJ2u2PBxZxSbOdlg5V6dPR8 HS+kZ2avUtGobCxvDgfCCZWjsXQfkvA7uYHnX7dko4RY2LAfIWCwbjPMxFvoB+hAe3v+ rJRkr17xRPRXmflu1MYZ32otOujdfFmQT7yW6WF6G4YDHfaOq9oeejVvAsNg6GbNRebk GSKSssdlrvk1Y/RViLLuwVbpNvP6bUGHFbRfhlEhFtGHm/S5Ii/YnfjWPSJqBJeO0SZh gosQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=2fYnWEKG+mtlk6pd5vLfTLrVUREvhZpJc3UmGG6AEjc=; fh=m5GgctCgHkU1HyMYdr7XZK6ftE7VCLP3i2VVC4AeZjM=; b=AgY0rYOar09951L4Z3DQ8RJfmMVu6l4p41XiskkRobg3/nKfiN1pjpHuXAFDMGUnWf jfNY8TZcgaXUpqXfT97UxDdE82Ola0EUdzcUrnCl5wEcvaNCj2CIi03XHyDElUm1Pz0D BKg46p7qBjFqIGiGiejC+UdvCFAFfYo5POLRXWIqxtQ98o2HHBvvNNZxbqTv0YceFtKp TZlWB3hKQLGzDhgKRyeH7h/3kUPfmpTMWn/Mjm7llffxKnCUx+SDATe6f++FQiuQwso6 n7qWm60Xd6gmAO0laCuRfwWNCZEu9Ce2Xy1DOr7FCg83sXAsT7IBkAEkgEjMbF7hl73e 2HAg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@martin-st.20230601.gappssmtp.com header.s=20230601 header.b=wUZMIRMv; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 4fb4d7f45d1cf-57a98f04254si513679a12.1.2024.06.05.04.47.14; Wed, 05 Jun 2024 04:47:15 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@martin-st.20230601.gappssmtp.com header.s=20230601 header.b=wUZMIRMv; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 5D3A968D2A2; Wed, 5 Jun 2024 14:47:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lf1-f44.google.com (mail-lf1-f44.google.com [209.85.167.44]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4369668D2A2 for ; Wed, 5 Jun 2024 14:47:04 +0300 (EEST) Received: by mail-lf1-f44.google.com with SMTP id 2adb3069b0e04-52b8b638437so2450553e87.3 for ; Wed, 05 Jun 2024 04:47:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=martin-st.20230601.gappssmtp.com; s=20230601; t=1717588023; x=1718192823; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=oq4bCTX1WA+P0UCRTCutl7EM992vu7TXs3XTf7x6wvg=; b=wUZMIRMv9qJoQy8f0kLVnRXdAW0II2ChOEM8uGD8kYux4v+cD1MPPFzFrsn1o463/i B2c4tGf+tcKdvKxQceT1AD7hZEUSDR1pVeQlzxJ7EOCTsqAKM2MzUIzTl+ddwhR7DReG w1IxX64U0My3mZ3bOzK3SGnWZysQOZb+LPS72/GFDfx7XsFBTQRV5ldScJ45DbBklRU8 TjOIjdJYCGbEO0dCE4HUcXfiXd4TmEXhmFts60kZLCoT/fJveyEhuIDlvVaxOOZez1SH JgrSxVeyZdA7xWJj71QaRJPrOq8fSKyVisnrTSlROE+Ki9/E8YgcuDXpS/Ntt1eHJJuA hAdQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717588023; x=1718192823; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=oq4bCTX1WA+P0UCRTCutl7EM992vu7TXs3XTf7x6wvg=; b=rnuzwz5VNHwIyQigZisRCdeie/JhR8wB0CZisFt3QSC4yf6Hhla2PZVxEu1vRINbct VhSPRBoV3plW1PyR56OIZpoynxltcAedXZdKQXkMedTdkEAuq3UpCHLl9NI8Xtu/dKhS 8DP6p8dcG7MAdo/Pb9uc2nMci6oZ2q1cDRy0tJMmRpJ/5vHfRdukOd0DiNLn6TT3n5zx 1pAnKzM89OJehLIbca5lPfN80h02PZ8LONiGiMitP+D5KsD4Y55kVqS3CN8P9nFooq64 ZlBHEbJzY8Ld8+ULI4JXAX+wFqQnXDmMfB7bEUPR+79TP9kHTBjUV9iBftE9GyH2RBPQ iZUA== X-Gm-Message-State: AOJu0Yyrgf+c2QT9Bbpz7mlO82HhQxsMw6p1/swIDzL3gJyklpT9dV3Z vShVnkUYaxZso3V9RqJv9WeE4du16y381DqjunP/DPs8rm3RppBKLIjQm/wjvdpW7Nljp2GGpiz Umg== X-Received: by 2002:ac2:53a8:0:b0:52b:92a7:78e0 with SMTP id 2adb3069b0e04-52bab4dcfd6mr1118251e87.18.1717588023075; Wed, 05 Jun 2024 04:47:03 -0700 (PDT) Received: from localhost (host-114-191.parnet.fi. [77.234.114.191]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-52b84d3f61csm1762145e87.109.2024.06.05.04.47.02 (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 05 Jun 2024 04:47:02 -0700 (PDT) From: =?utf-8?q?Martin_Storsj=C3=B6?= To: ffmpeg-devel@ffmpeg.org Date: Wed, 5 Jun 2024 14:47:02 +0300 Message-Id: <20240605114702.71176-1-martin@martin.st> X-Mailer: git-send-email 2.39.3 (Apple Git-146) MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2] movenc: Add an option for hiding fragments at the end X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: dennis@obsproject.com, timo@rothenpieler.org Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: FznaOKit8E/m This allows ending up with a normal, non-fragmented file when the file is finished, while keeping the file readable if writing is aborted abruptly at any point. (Normally when writing a mov/mp4 file, the unfinished file is completely useless unless it is finished properly.) This results in a file where the mdat atom contains (and hides) all the moof atoms that were part of the fragmented file structure initially. --- v2: Made the flag implicitly set FF_MOV_FLAG_FRAGMENT (as it makes no sense without it). Updated the description of the flag to "Write a fragmented file that is converted to non-fragmented at the end". Kept the flag named "hide_fragments", but I'm also pondering if we maybe should go for a name like "hybrid_fragmented" or so, as a better description of _what_ it produces, as opposed to _how_ it does things. (One could also consider "hybrid_mp4", but even if mp4 is the main thing, the same also goes for mov and a bunch of other related formats.) --- doc/muxers.texi | 7 ++++ libavformat/movenc.c | 62 ++++++++++++++++++++++++++++++++--- libavformat/movenc.h | 4 ++- libavformat/version.h | 4 +-- tests/fate/lavf-container.mak | 3 +- tests/ref/lavf/mov_hide_frag | 3 ++ 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 tests/ref/lavf/mov_hide_frag diff --git a/doc/muxers.texi b/doc/muxers.texi index 6340c8e54d..e313b5e631 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -569,6 +569,13 @@ experimental, may be renamed or changed, do not use from scripts. @item write_gama write deprecated gama atom + +@item hide_fragments +After writing a fragmented file, convert it to a regular, non-fragmented +file at the end. This keeps the file readable while it is being +written, and makes it recoverable if the process of writing the file +gets aborted uncleanly, while still producing an easily seekable +and widely compatible non-fragmented file in the end. @end table @item movie_timescale @var{scale} diff --git a/libavformat/movenc.c b/libavformat/movenc.c index d1517870fc..da16a9dae5 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -110,6 +110,7 @@ static const AVOption options[] = { { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, { "write_colr", "Write colr atom even if the color info is unspecified (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "hide_fragments", "Write a fragmented file that is converted to non-fragmented at the end", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_HIDE_FRAGMENTS}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "mov_gamma", "gamma value for gama atom", offsetof(MOVMuxContext, gamma), AV_OPT_TYPE_FLOAT, {.dbl = 0.0 }, 0.0, 10, AV_OPT_FLAG_ENCODING_PARAM}, { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, @@ -5993,10 +5994,30 @@ static int mov_write_squashed_packets(AVFormatContext *s) return 0; } -static int mov_finish_fragment(MOVTrack *track) +static int mov_finish_fragment(MOVMuxContext *mov, MOVTrack *track, + int64_t ref_pos) { + int i; if (!track->entry) return 0; + if (mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) { + for (i = 0; i < track->entry; i++) + track->cluster[i].pos += ref_pos + track->data_offset; + if (track->cluster_written == 0 && !(mov->flags & FF_MOV_FLAG_EMPTY_MOOV)) { + // First flush. If this was a case of not using empty moov, reset chunking. + for (i = 0; i < track->entry; i++) { + track->cluster[i].chunkNum = 0; + track->cluster[i].samples_in_chunk = track->cluster[i].entries; + } + } + if (av_reallocp_array(&track->cluster_written, + track->entry_written + track->entry, + sizeof(*track->cluster))) + return AVERROR(ENOMEM); + memcpy(&track->cluster_written[track->entry_written], + track->cluster, track->entry * sizeof(*track->cluster)); + track->entry_written += track->entry; + } track->entry = 0; track->entries_flushed = 0; track->end_reliable = 0; @@ -6007,7 +6028,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) { MOVMuxContext *mov = s->priv_data; int i, first_track = -1; - int64_t mdat_size = 0; + int64_t mdat_size = 0, mdat_start = 0; int ret; int has_video = 0, starts_with_key = 0, first_video_track = 1; @@ -6113,7 +6134,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->moov_written = 1; mov->mdat_size = 0; for (i = 0; i < mov->nb_tracks; i++) - mov_finish_fragment(&mov->tracks[i]); + mov_finish_fragment(mov, &mov->tracks[i], 0); avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); return 0; } @@ -6182,9 +6203,10 @@ static int mov_flush_fragment(AVFormatContext *s, int force) avio_wb32(s->pb, mdat_size + 8); ffio_wfourcc(s->pb, "mdat"); + mdat_start = avio_tell(s->pb); } - mov_finish_fragment(&mov->tracks[i]); + mov_finish_fragment(mov, &mov->tracks[i], mdat_start); if (!mov->frag_interleave) { if (!track->mdat_buf) continue; @@ -7169,6 +7191,7 @@ static void mov_free(AVFormatContext *s) else if (track->tag == MKTAG('t','m','c','d') && mov->nb_meta_tmcd) av_freep(&track->par); av_freep(&track->cluster); + av_freep(&track->cluster_written); av_freep(&track->frag_info); av_packet_free(&track->cover_image); @@ -7363,6 +7386,9 @@ static int mov_init(AVFormatContext *s) mov->flags |= FF_MOV_FLAG_FRAGMENT; /* Set other implicit flags immediately */ + if (mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) + mov->flags |= FF_MOV_FLAG_FRAGMENT; + if (mov->mode == MODE_ISM) mov->flags |= FF_MOV_FLAG_EMPTY_MOOV | FF_MOV_FLAG_SEPARATE_MOOF | FF_MOV_FLAG_FRAGMENT | FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS; @@ -7887,6 +7913,11 @@ static int mov_write_header(AVFormatContext *s) FF_MOV_FLAG_FRAG_EVERY_FRAME)) && !mov->max_fragment_duration && !mov->max_fragment_size) mov->flags |= FF_MOV_FLAG_FRAG_KEYFRAME; + if (mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) { + avio_wb32(pb, 8); // placeholder for extended size field (64 bit) + ffio_wfourcc(pb, mov->mode == MODE_MOV ? "wide" : "free"); + mov->mdat_pos = avio_tell(pb); + } } else if (mov->mode != MODE_AVIF) { if (mov->flags & FF_MOV_FLAG_FASTSTART) mov->reserved_header_pos = avio_tell(pb); @@ -8090,13 +8121,34 @@ static int mov_write_trailer(AVFormatContext *s) } } - if (!(mov->flags & FF_MOV_FLAG_FRAGMENT)) { + if (!(mov->flags & FF_MOV_FLAG_FRAGMENT) || + mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) { + if (mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) { + mov_flush_fragment(s, 1); + mov->mdat_size = avio_tell(pb) - mov->mdat_pos - 8; + for (i = 0; i < mov->nb_tracks; i++) { + MOVTrack *track = &mov->tracks[i]; + track->data_offset = 0; + av_free(track->cluster); + track->cluster = track->cluster_written; + track->entry = track->entry_written; + track->cluster_written = NULL; + track->entry_written = 0; + track->chunkCount = 0; // Force build_chunks to rebuild the list of chunks + } + // Clear the empty_moov flag, as we do want the moov to include + // all the samples at this point. + mov->flags &= ~FF_MOV_FLAG_EMPTY_MOOV; + } + moov_pos = avio_tell(pb); /* Write size of mdat tag */ if (mov->mdat_size + 8 <= UINT32_MAX) { avio_seek(pb, mov->mdat_pos, SEEK_SET); avio_wb32(pb, mov->mdat_size + 8); + if (mov->flags & FF_MOV_FLAG_HIDE_FRAGMENTS) + ffio_wfourcc(pb, "mdat"); // overwrite the original moov into a mdat } else { /* overwrite 'wide' placeholder atom */ avio_seek(pb, mov->mdat_pos - 8, SEEK_SET); diff --git a/libavformat/movenc.h b/libavformat/movenc.h index 08d580594d..59daf8946b 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -85,7 +85,7 @@ typedef struct MOVFragmentInfo { typedef struct MOVTrack { int mode; - int entry; + int entry, entry_written; unsigned timescale; uint64_t time; int64_t track_duration; @@ -114,6 +114,7 @@ typedef struct MOVTrack { int vos_len; uint8_t *vos_data; MOVIentry *cluster; + MOVIentry *cluster_written; unsigned cluster_capacity; int audio_vbr; int height; ///< active picture (w/o VBI) height for D-10/IMX @@ -282,6 +283,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SKIP_SIDX (1 << 21) #define FF_MOV_FLAG_CMAF (1 << 22) #define FF_MOV_FLAG_PREFER_ICC (1 << 23) +#define FF_MOV_FLAG_HIDE_FRAGMENTS (1 << 24) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); diff --git a/libavformat/version.h b/libavformat/version.h index 4687cd857c..af7d0a1024 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,8 +31,8 @@ #include "version_major.h" -#define LIBAVFORMAT_VERSION_MINOR 3 -#define LIBAVFORMAT_VERSION_MICRO 104 +#define LIBAVFORMAT_VERSION_MINOR 4 +#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ diff --git a/tests/fate/lavf-container.mak b/tests/fate/lavf-container.mak index d84117c50f..035c394c09 100644 --- a/tests/fate/lavf-container.mak +++ b/tests/fate/lavf-container.mak @@ -5,7 +5,7 @@ FATE_LAVF_CONTAINER-$(call ENCDEC, FLV, FLV) + FATE_LAVF_CONTAINER-$(call ENCDEC, RAWVIDEO, FILMSTRIP) += flm FATE_LAVF_CONTAINER-$(call ENCDEC2, MPEG2VIDEO, PCM_S16LE, GXF) += gxf gxf_pal gxf_ntsc FATE_LAVF_CONTAINER-$(call ENCDEC2, MPEG4, MP2, MATROSKA) += mkv mkv_attachment -FATE_LAVF_CONTAINER-$(call ENCDEC2, MPEG4, PCM_ALAW, MOV) += mov mov_rtphint ismv +FATE_LAVF_CONTAINER-$(call ENCDEC2, MPEG4, PCM_ALAW, MOV) += mov mov_rtphint mov_hide_frag ismv FATE_LAVF_CONTAINER-$(call ENCDEC, MPEG4, MOV) += mp4 FATE_LAVF_CONTAINER-$(call ENCDEC2, MPEG1VIDEO, MP2, MPEG1SYSTEM MPEGPS) += mpg FATE_LAVF_CONTAINER-$(call ENCDEC , FFV1, MXF) += mxf_ffv1 @@ -51,6 +51,7 @@ fate-lavf-mkv: CMD = lavf_container "" "-c:a mp2 -c:v mpeg4 -ar 44100 -threads 1 fate-lavf-mkv_attachment: CMD = lavf_container_attach "-c:a mp2 -c:v mpeg4 -threads 1 -f matroska" fate-lavf-mov: CMD = lavf_container_timecode "-movflags +faststart -c:a pcm_alaw -c:v mpeg4 -threads 1" fate-lavf-mov_rtphint: CMD = lavf_container "" "-movflags +rtphint -c:a pcm_alaw -c:v mpeg4 -threads 1 -f mov" +fate-lavf-mov_hide_frag: CMD = lavf_container "" "-movflags +frag_keyframe+hide_fragments -c:a pcm_alaw -c:v mpeg4 -threads 1 -f mov" fate-lavf-mp4: CMD = lavf_container_timecode "-c:v mpeg4 -an -threads 1" fate-lavf-mpg: CMD = lavf_container_timecode "-ar 44100 -threads 1" fate-lavf-mxf: CMD = lavf_container_timecode "-af aresample=48000:tsf=s16p -bf 2 -threads 1" diff --git a/tests/ref/lavf/mov_hide_frag b/tests/ref/lavf/mov_hide_frag new file mode 100644 index 0000000000..af3af5b171 --- /dev/null +++ b/tests/ref/lavf/mov_hide_frag @@ -0,0 +1,3 @@ +4871796f41234350f1b050317d0288a3 *tests/data/lavf/lavf.mov_hide_frag +358508 tests/data/lavf/lavf.mov_hide_frag +tests/data/lavf/lavf.mov_hide_frag CRC=0xbb2b949b