From patchwork Thu Jan 12 20:45:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vignesh Venkat X-Patchwork-Id: 39987 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:bc95:b0:ad:ade2:bfd2 with SMTP id fx21csp5833455pzb; Thu, 12 Jan 2023 12:46:05 -0800 (PST) X-Google-Smtp-Source: AMrXdXtnepRBvnYNZqaC6xk9S1U8Q/x6V88T1R5GZORVIgCwfMUkCAvvTNzBWjg4WpQeY0cUzGHK X-Received: by 2002:a17:907:8b0a:b0:7c0:ae13:7407 with SMTP id sz10-20020a1709078b0a00b007c0ae137407mr76072257ejc.3.1673556365437; Thu, 12 Jan 2023 12:46:05 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1673556365; cv=none; d=google.com; s=arc-20160816; b=cjgaO6NCiWExdtwuOuf6WJopmQXzZZggrsI3iqrSOMjL+DjOZDhWGgIpE3KMNHmyTC wkXH7z2MRVDHRmwfoO2PSkgmuSCnEU3C+vkapav5G8CbUmBaV872E4JjoCdZGE6/INKs l5D9U2ZL/O1tHEWQRm6bYr/ov6EX/x/nF5nU2RMUp3DOToM8sDMPCZH//d7wTqd0f+BL 4n4iT/cwnzQNPetpdKNrc7cgUcgVFsKKniHPoIHYykaHkiZm2EsH6Vxwy9od3zyFritQ +yZCR31f0xHs1hIbSXju79Q+k7z4R7Ns2p7LrvKta10DwW6eh0UqGaSxDibNKkgmKmpN fnfA== 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:to:from:message-id:references :mime-version:in-reply-to:date:dkim-signature:delivered-to; bh=TY7TZYMVsAoCGegLRvgNB0CkevXOtzRLovcBVkU1LcY=; b=oYF/5kTr/9aIoLsYrZo73aaqYfGybN9w1C85+zWT1VVfZZvOqppSEBIM+dYhOCiHMg ZsN3IGPvafPnZ5jkgucEJi/TILZiCyoMtB3TWx4FL5S2SsXcpldPMw/mDJxOZgVVYkcv UdHLASxQSvws4UxZm+ZkpL1zE9YKzjYLk+TeTj/yMoxogNgYYSCcwZqWHns5/goKxG74 aT7Z+kV3xddURz6grrcW+WpuC7PNBmYN7OMkxcYHwSe1obP7sgI18IN1T1FVK8pkMkNn ZCYn7UErRT1Bu6IzCj+R7L0HLPwGlrUIXHNDSDq1S8ozRBkB/dqK+2ltlb4zLzNEOxOf CAFg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@google.com header.s=20210112 header.b=KFS4LAg4; 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 du8-20020a17090772c800b007c4f4d0427dsi18904051ejc.409.2023.01.12.12.46.04; Thu, 12 Jan 2023 12:46:05 -0800 (PST) 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=@google.com header.s=20210112 header.b=KFS4LAg4; 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 5D47C68BC19; Thu, 12 Jan 2023 22:46:00 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-yw1-f201.google.com (mail-yw1-f201.google.com [209.85.128.201]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2DF8B68AFBB for ; Thu, 12 Jan 2023 22:45:53 +0200 (EET) Received: by mail-yw1-f201.google.com with SMTP id 00721157ae682-349423f04dbso207738967b3.13 for ; Thu, 12 Jan 2023 12:45:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+Jd/PIzQPCSUljXrasgtKIcg+ObuXxujhzLNUiLCw+A=; b=KFS4LAg4hGdekwd9oWIeEbIN/lTCVhb1bADvNobTr4apj5Az3NiLl+d4W4mz53oM60 xTeUQaTV7c+7ngAKlP9FXtuGf6YnfIFEPDZtJy2yBnghf/Eu2q224BC/nC+T62GE9mRy l8cMLPByk2GWFbOmJ3xxf7Mb/mJ69Rdryr9GRPP9cnf43lx9Bt44HH1HhLrdTFKRgNtK t5dfbWq1pnVtXtmwU4xaFmUpJHjdpW4w98lEIiLiaMyBNCVJB4cswO652WQW6i+sOFBK PngLDLtU/YUd3uWVcLtj8eKqU0E0YwnnIU5faDUG/Wz79GQ3nCY8TLQKx8IMFvKcqdGP dL7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+Jd/PIzQPCSUljXrasgtKIcg+ObuXxujhzLNUiLCw+A=; b=XR1Bn7FoCycevXVEFn/yUjd84nUz07ctJULbkb3mFy/RSVpYg4DnMHUmnWWTVtAk2W gRzw2/xu0j95juIiJZNhT5KYneZE4V6Q6MQOcMMvPv5gz57UGYznoDtq10eCdnOBQkSW IDcPYlZvaU6VYUFkJzXf3YdWn5IGeiXfduYxoWp+ajSqRDkdKxOvqNXawY1XyyWzAJun ej+GkSSRAov5/tac73075qZu0/m3dh1/4OkgbkUzKSKslSpB/0BXHZK1CULSIECkdXV2 xDnywIb+0yZp7+5j2ybxU+ul/T3FSfUfdeGN2IOk2lwtYB+DVTtIBepHkhi/v/yMQMyd bZjg== X-Gm-Message-State: AFqh2ko6lDOb9deHaJU654u3DusNulDIQ6y5s+Dc64Vlu4EaONQuFcK2 IPNWH99cCLhwq9GrSW3wZZ7Te04ITw+z2qHdzyyW6X3prsjv0taFomq6Z5iO/QRTfvqjZmIUFf+ nyrFsMWujvBu7XwRwxV98jT97Nt6zdnkuP4hXTHADX7uKth6TVrJ4pF0X5OY+fWLFOeE5 X-Received: from vigneshv3.mtv.corp.google.com ([2620:0:1000:2511:b9e0:d21b:19e8:5a37]) (user=vigneshv job=sendgmr) by 2002:a0d:d753:0:b0:3a8:b862:d82e with SMTP id z80-20020a0dd753000000b003a8b862d82emr2589544ywd.200.1673556351472; Thu, 12 Jan 2023 12:45:51 -0800 (PST) Date: Thu, 12 Jan 2023 12:45:45 -0800 In-Reply-To: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.39.0.314.g84b9a713c41-goog Message-ID: <20230112204545.3336588-1-vigneshv@google.com> From: Vignesh Venkatasubramanian To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 2/2] avformat/movenc: Add loop parameter to animated AVIF 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: Vignesh Venkatasubramanian Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: TPBTYPk0aEbY The HEIF specification permits specifying the looping behavior of animated sequences by using the EditList (elst) box. The track duration will be set to the total duration of all the loops (or infinite) and the duration of a single loop will be set in the edit list box. The default behavior is to loop infinitely. Compliance verification: * This was added in libavif recently [1] and the files produced by ffmpeg after this change have EditList boxes similar to the ones produced by libavif (and avifdec is able to parse the loop count as intended). * ComplianceWarden is ok with the produced files. * Chrome is able to play back the produced files. [1] https://github.com/AOMediaCodec/libavif/commit/4d2776a3af53ae1aefdaed463b75ba12fd9cf8c2 Signed-off-by: Vignesh Venkatasubramanian --- libavformat/movenc.c | 35 +++++++++++++++++++++++++++++++---- libavformat/movenc.h | 1 + 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 36c76f7f60..8d31317838 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -3287,7 +3287,7 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, int64_t duration = av_rescale_rnd(calc_pts_duration(mov, track), mov->movie_timescale, track->timescale, AV_ROUND_UP); - int version = duration < INT32_MAX ? 0 : 1; + int version; int flags = MOV_TKHD_FLAG_IN_MOVIE; int group = 0; @@ -3295,6 +3295,14 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, size_t display_matrix_size; int i; + if (mov->mode == MODE_AVIF) + if (!mov->avif_loop_count) + duration = INT64_MAX; + else + duration *= mov->avif_loop_count; + + version = duration < INT32_MAX ? 0 : 1; + if (st) { if (mov->per_stream_grouping) group = st->index; @@ -3414,7 +3422,10 @@ static int mov_write_tapt_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } -// This box seems important for the psp playback ... without it the movie seems to hang +// This box is written in the following cases: +// * Seems important for the psp playback. Without it the movie seems to hang. +// * Used for specifying the looping behavior of animated AVIF (as specified +// in Section 9.6 of the HEIF specification ISO/IEC 23008-12). static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { @@ -3425,6 +3436,7 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, int entry_size, entry_count, size; int64_t delay, start_ct = track->start_cts; int64_t start_dts = track->start_dts; + int flags = 0; if (track->entry) { if (start_dts != track->cluster[0].dts || start_ct != track->cluster[0].cts) { @@ -3440,6 +3452,17 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, delay = av_rescale_rnd(start_dts + start_ct, mov->movie_timescale, track->timescale, AV_ROUND_DOWN); + + if (mov->mode == MODE_AVIF) { + delay = 0; + // Section 9.6.3 of ISO/IEC 23008-12: flags specifies repetition of the + // edit list as follows: (flags & 1) equal to 0 specifies that the edit + // list is not repeated, while (flags & 1) equal to 1 specifies that the + // edit list is repeated. + flags = mov->avif_loop_count != 1; + start_ct = 0; + } + version |= delay < INT32_MAX ? 0 : 1; entry_size = (version == 1) ? 20 : 12; @@ -3452,7 +3475,7 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, size - 8); ffio_wfourcc(pb, "elst"); avio_w8(pb, version); - avio_wb24(pb, 0); /* flags */ + avio_wb24(pb, flags); /* flags */ avio_wb32(pb, entry_count); if (delay > 0) { /* add an empty edit to delay presentation */ @@ -3469,7 +3492,7 @@ static int mov_write_edts_tag(AVIOContext *pb, MOVMuxContext *mov, avio_wb32(pb, -1); } avio_wb32(pb, 0x00010000); - } else { + } else if (mov->mode != MODE_AVIF) { /* Avoid accidentally ending up with start_ct = -1 which has got a * special meaning. Normally start_ct should end up positive or zero * here, but use FFMIN in case dts is a small positive integer @@ -3670,6 +3693,9 @@ static int mov_write_trak_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext "Not writing any edit list even though one would have been required\n"); } + if (mov->is_animated_avif) + mov_write_edts_tag(pb, mov, track); + if (track->tref_tag) mov_write_tref_tag(pb, track); @@ -7761,6 +7787,7 @@ static const AVCodecTag codec_f4v_tags[] = { static const AVOption avif_options[] = { { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "loop", "Number of times to loop animated AVIF: 0 - infinite loop", offsetof(MOVMuxContext, avif_loop_count), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 }, { NULL }, }; static const AVCodecTag codec_avif_tags[] = { diff --git a/libavformat/movenc.h b/libavformat/movenc.h index c6b3313deb..e85d83abdb 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -249,6 +249,7 @@ typedef struct MOVMuxContext { int64_t avif_extent_pos[2]; // index 0 is YUV and 1 is Alpha. int avif_extent_length[2]; // index 0 is YUV and 1 is Alpha. int is_animated_avif; + int avif_loop_count; } MOVMuxContext; #define FF_MOV_FLAG_RTP_HINT (1 << 0)