From patchwork Wed Jun 26 10:57:38 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: 50154 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a59:ccd2:0:b0:482:c625:d099 with SMTP id q18csp347123vqv; Wed, 26 Jun 2024 03:58:28 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCVMbL5nFZkSWcCiis62ww6VOtJyC++4jaCJRW7atkYoWx6U2Vf5rKU8P3PriFFNbqekTWd9gV7+nMBnIsDxymqt5qFn5W8XrCjv1w== X-Google-Smtp-Source: AGHT+IE2PywM+V9JUL5vZZABCzE1h9WWEIJXYx+GdpeGZSGQExEDz5D0EGQVD7CvLbt/l1tsgQp5 X-Received: by 2002:a17:906:694b:b0:a6f:5815:f5e6 with SMTP id a640c23a62f3a-a7245b4cbabmr599908266b.8.1719399508258; Wed, 26 Jun 2024 03:58:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1719399508; cv=none; d=google.com; s=arc-20160816; b=I6PmASX8/wKe1E0lSF58XXxBObq28nwRYp9aa9cXjEBwgBb5dlVR5vbfQjrjxbW3IA wmRMbapX07XRVdkeKLhvYGT/lN8ZGPG41ATDZCURX8ZBPgDSj9CE4UbqGNdHH2WNeQPG OnulsBWhe0nk5NZ4Z+3AGtzxfif0tw0RrxFbxIpgvF7o49+eQ1yjmhOe4UAbLtDfnpeV mX2TWrNpVy4PY2Ufx0A8bUW4PZV76s/pqWEhh8GwghjVTPnFFdyhD9UcJXH9woOEXPNG s11U7qYAC9pYODo0du3HQT2Vqj9MOooKQj+sZIYxQ6Kggpg3QlHkFgvG5DgOmdtN2fmY fR2A== 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:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=n26gmJEyOoTwAu9akQs98ZfhLnCbLzsv8DlpqOeZ/Vs=; fh=w8JR1T/5WquwCqgnlKgniZcITMXOtCHDzF0NyyyGKZI=; b=O+qykhK+s26acnHutRtjtDmAnfnyvAItVi4gZ5OUZJ3kbV5zN8qsiQ7XxGTfY076Sg t1NcopILNXekypGu/DVjOsQY3/wIsQ35bKtYk6Y8PcZZeddrsAkPjHbHKScGwAt+i9PX 79AvqueHrRPwNNsTbneRe31d5FK4HPfmKUIFIyz0OMj55wKsCEIsL0r9lfn32JRbm4QL bacM/uz+O1X8jrlvxUTi31oAsFvG8Agrm9LLquC3/a9eAG7Nd97dhzH8p4hWdv+/6ylt 5h/LikFIpjx6pCKtMu8JGpvkx9mlpi7HzFeed+FXzqKzT+kQQaIHSdTCPDhCTv/r6H+/ vhlg==; 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=X3gHE3tm; 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 a640c23a62f3a-a726759983dsi192728566b.380.2024.06.26.03.58.27; Wed, 26 Jun 2024 03:58:28 -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=X3gHE3tm; 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 5CDDB68D605; Wed, 26 Jun 2024 13:57:51 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BBB6668C0EB for ; Wed, 26 Jun 2024 13:57:43 +0300 (EEST) Received: by mail-lf1-f42.google.com with SMTP id 2adb3069b0e04-52cecba8d11so2253092e87.1 for ; Wed, 26 Jun 2024 03:57:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=martin-st.20230601.gappssmtp.com; s=20230601; t=1719399463; x=1720004263; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Jh2xkXJYfSr5TvV7RyKVBkBIu2R06kQHRKg9WaIJCKU=; b=X3gHE3tmP16qNW+FNmQ4I+NtFletAxIuDdBq50XfAaE8xdRVha5x8ld5CxOyDdtYod cGwHZmVpKWaSQ2B+6A4lkZnLJBeC47Y5vqUoyVF5S38hBOyQ1UT8IzKdtX2SajhwHLnX WXkjbtUlg8bEzd6HGeZCWKf9ZmxPeHhiyfhXajusSgpPHc3pwSM42XKtDzcRmTrsMXCk qIk8Ou+jz3gZBf7AGcqOJKeTks8rD19sxrPl9Ux2dLawsqcajvfi/SLfH2uftVVIzZVE p7XDnGa8Erq+dKnvZxvwsjXReF5BSGdImL++IjUpLNMjvrptWDI4fzE1jTeo8yXdlEX3 FGOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719399463; x=1720004263; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Jh2xkXJYfSr5TvV7RyKVBkBIu2R06kQHRKg9WaIJCKU=; b=nKlNpG239vzeZ815hP/dFZLAAGBIvdqG88Jz6flGbettZdaOB9tEoQduGdG3CbjXtm 536tEo7gtMnzkmYGSq/T6TlYyrC4raAHm6eUpnH92y4WiiTekp7hqZ6QoCVO1sa3OHUB IuxrMtuw8+WPS7azYM89ynnbyZZtnDehHhnfO9g4/uydfCEaJeNF7b0D5Ovwa6QB+YiP sNuzbuOZYicZm7AW3AgpVPgtpGYmPuiQpLJrZkGfEj6G3V+o2LG4I8cVHGs/S49UsK2C WKZbhS0JZNwV9v0WGpfTIJsFTCRwZzw6JSR7kME0YzNkKcvlfXIsrG8y32UtpDoY7qe7 rDBg== X-Gm-Message-State: AOJu0YxHWDH8Sy4td2ZHIGMpQ8nrm6jnlKXHZcFno7zK3u0wJl8g8HKI 77M0XtYTbbk1UeAP04FUj3UdSw5JbbKKDkiPe2tsU+UD/3utxS0BAE4DnncdP6sbuIm7w3RoNpI ZjQ== X-Received: by 2002:a19:6b15:0:b0:52c:d085:9978 with SMTP id 2adb3069b0e04-52ce1864589mr6236545e87.62.1719399461700; Wed, 26 Jun 2024 03:57:41 -0700 (PDT) Received: from localhost (host-114-191.parnet.fi. [77.234.114.191]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-52cd644c537sm1524670e87.281.2024.06.26.03.57.41 (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 26 Jun 2024 03:57:41 -0700 (PDT) From: =?utf-8?q?Martin_Storsj=C3=B6?= To: ffmpeg-devel@ffmpeg.org Date: Wed, 26 Jun 2024 13:57:38 +0300 Message-Id: <20240626105738.58427-5-martin@martin.st> X-Mailer: git-send-email 2.39.3 (Apple Git-146) In-Reply-To: <20240626105738.58427-1-martin@martin.st> References: <20240626105738.58427-1-martin@martin.st> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 5/5] hlsenc: Calculate the average and actual maximum bitrate of segments 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: Steven Liu Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: pw5dqJOtAK9y Previously, the bitrate advertised in the master playlist would only be based on the nominal values in either AVCodecParameters bit_rate, or via AVCPBProperties max_bitrate. On top of this, a fudge factor of 10% is added, to account for container overhead. Neither of these bitrates may be known, and if the encoder is running in VBR mode, there is no such value to be known. And the container overhead may be more or less than the given constant factor of 10%. Instead, calculate the maximum bitrate per segment based on what actually gets output from the muxer, and average bitrate across all segments. When muxing of the file finishes, update the master playlist with these values, exposing both the maximum (which previously was a guesstimate based on the nominal values) via EXT-X-STREAM-INF BANDWIDTH, and the average via EXT-X-STREAM-INF AVERAGE-BANDWIDTH. This makes it possible to use the hlsenc muxer with VBR encodes, for VOD style muxing. --- libavformat/dashenc.c | 4 ++-- libavformat/hlsenc.c | 47 ++++++++++++++++++++++++++++----------- libavformat/hlsplaylist.c | 3 +++ libavformat/hlsplaylist.h | 1 + 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 8c14aa746e..d4a6fe0304 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1322,7 +1322,7 @@ static int write_manifest(AVFormatContext *s, int final) av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); - ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, + ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, 0, playlist_file, agroup, codec_str, NULL, NULL); } @@ -1348,7 +1348,7 @@ static int write_manifest(AVFormatContext *s, int final) continue; av_strlcpy(codec_str, os->codec_str, sizeof(codec_str)); get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); - ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, + ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, 0, playlist_file, NULL, codec_str, NULL, NULL); } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 7c8c886fc3..511ae40f8f 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -150,6 +150,11 @@ typedef struct VariantStream { int discontinuity; int reference_stream_index; + int64_t total_size; + double total_duration; + int64_t avg_bitrate; + int64_t max_bitrate; + HLSSegment *segments; HLSSegment *last_segment; HLSSegment *old_segments; @@ -1108,6 +1113,16 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls, if (!en) return AVERROR(ENOMEM); + vs->total_size += size; + vs->total_duration += duration; + if (duration > 0) { + int cur_bitrate = (int)(8 * size / duration); + if (cur_bitrate > vs->max_bitrate) + vs->max_bitrate = cur_bitrate; + } + if (vs->total_duration > 0) + vs->avg_bitrate = (int)(8 * vs->total_size / vs->total_duration); + en->var_stream_idx = vs->var_stream_idx; ret = sls_flags_filename_process(s, hls, vs, en, duration, pos, size); if (ret < 0) { @@ -1362,14 +1377,15 @@ static int64_t get_stream_bit_rate(AVStream *stream) } static int create_master_playlist(AVFormatContext *s, - VariantStream * const input_vs) + VariantStream * const input_vs, + int final) { HLSContext *hls = s->priv_data; VariantStream *vs, *temp_vs; AVStream *vid_st, *aud_st; AVDictionary *options = NULL; unsigned int i, j; - int ret, bandwidth; + int ret, bandwidth, avg_bandwidth; const char *m3u8_rel_name = NULL; const char *vtt_m3u8_rel_name = NULL; const char *ccgroup; @@ -1389,8 +1405,8 @@ static int create_master_playlist(AVFormatContext *s, return 0; } else { /* Keep publishing the master playlist at the configured rate */ - if (&hls->var_streams[0] != input_vs || !hls->master_publish_rate || - input_vs->number % hls->master_publish_rate) + if ((&hls->var_streams[0] != input_vs || !hls->master_publish_rate || + input_vs->number % hls->master_publish_rate) && !final) return 0; } @@ -1480,12 +1496,17 @@ static int create_master_playlist(AVFormatContext *s, } } - bandwidth = 0; - if (vid_st) - bandwidth += get_stream_bit_rate(vid_st); - if (aud_st) - bandwidth += get_stream_bit_rate(aud_st); - bandwidth += bandwidth / 10; + if (final) { + bandwidth = vs->max_bitrate; + avg_bandwidth = vs->avg_bitrate; + } else { + bandwidth = 0; + if (vid_st) + bandwidth += get_stream_bit_rate(vid_st); + if (aud_st) + bandwidth += get_stream_bit_rate(aud_st); + bandwidth += bandwidth / 10; + } ccgroup = NULL; if (vid_st && vs->ccgroup) { @@ -1514,11 +1535,11 @@ static int create_master_playlist(AVFormatContext *s, } if (!hls->has_default_key || !hls->has_video_m3u8) { - ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, + ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, avg_bandwidth, m3u8_rel_name, aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup); } else { if (vid_st) { - ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, + ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, avg_bandwidth, m3u8_rel_name, aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup, sgroup); } } @@ -1671,7 +1692,7 @@ fail: ff_rename(temp_vtt_filename, vs->vtt_m3u8_name, s); } if (ret >= 0 && hls->master_pl_name) - if (create_master_playlist(s, vs) < 0) + if (create_master_playlist(s, vs, last) < 0) av_log(s, AV_LOG_WARNING, "Master playlist creation failed\n"); return ret; diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 4f35d0388f..f8a6977702 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -71,6 +71,7 @@ void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, } void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, + int avg_bandwidth, const char *filename, const char *agroup, const char *codecs, const char *ccgroup, const char *sgroup) @@ -85,6 +86,8 @@ void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, } avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); + if (avg_bandwidth) + avio_printf(out, ",AVERAGE-BANDWIDTH=%d", avg_bandwidth); if (st && st->codecpar->width > 0 && st->codecpar->height > 0) avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width, st->codecpar->height); diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index c2744c227c..d7aa44d8dc 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -43,6 +43,7 @@ void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, const char *filename, const char *language, int name_id, int is_default); void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, + int avg_bandwidth, const char *filename, const char *agroup, const char *codecs, const char *ccgroup, const char *sgroup);