This patch is from https://ffmpeg.org//pipermail/ffmpeg-devel/2024-November/335575.html

Here is a proposed patch for portability of doc/t2h.pm for GNU Texinfo
7.1 and 7.1.90 (7.2 pretest).  I tested against 7.1 and 7.1.90 (7.2
pretest).  There is a difference in the headings compared to the website
version, maybe related to FA_ICONS not being set the same, but the
result seems correct.

I also renamed $element to $output_unit in ffmpeg_heading_command as in
new equivalent makeinfo/texi2any code the $element variable is the
$command variable in ffmpeg_heading_command, which is very confusing.  I
left as is the $command variable to have a patch easier to read, but it
could make sense to rename $command as $element later on.

The patch could also have effects with Texinfo 7.0, since some of the
changes are for that version, but that probably never show up because it
is for situations that may not exist in ffmpeg manuals (for example
@node without sectioning command), or because the code is robust to some
missing information (case of $heading_level in ffmpeg_heading_command
that was not set, as far as I can tell).

---
 doc/t2h.pm | 169 ++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 129 insertions(+), 40 deletions(-)

diff --git a/doc/t2h.pm b/doc/t2h.pm
index b7485e1f1e..1359960f27 100644
--- a/doc/t2h.pm
+++ b/doc/t2h.pm
@@ -54,12 +54,24 @@ sub get_formatting_function($$) {
 }
 
 # determine texinfo version
-my $program_version_num = version->declare(ff_get_conf('PACKAGE_VERSION'))->numify;
+my $package_version = ff_get_conf('PACKAGE_VERSION');
+$package_version =~ s/\+dev$//;
+my $program_version_num = version->declare($package_version)->numify;
 my $program_version_6_8 = $program_version_num >= 6.008000;
 
 # no navigation elements
 ff_set_from_init_file('HEADERS', 0);
 
+my %sectioning_commands = %Texinfo::Common::sectioning_commands;
+if (scalar(keys(%sectioning_commands)) == 0) {
+  %sectioning_commands = %Texinfo::Commands::sectioning_heading_commands;
+}
+
+my %root_commands = %Texinfo::Common::root_commands;
+if (scalar(keys(%root_commands)) == 0) {
+  %root_commands = %Texinfo::Commands::root_commands;
+}
+
 sub ffmpeg_heading_command($$$$$)
 {
     my $self = shift;
@@ -77,6 +89,9 @@ sub ffmpeg_heading_command($$$$$)
         return $result;
     }
 
+    # no need to set it as the $element_id is output unconditionally
+    my $heading_id;
+
     my $element_id = $self->command_id($command);
     $result .= "<a name=\"$element_id\"></a>\n"
         if (defined($element_id) and $element_id ne '');
@@ -84,24 +99,40 @@ sub ffmpeg_heading_command($$$$$)
     print STDERR "Process $command "
         .Texinfo::Structuring::_print_root_command_texi($command)."\n"
             if ($self->get_conf('DEBUG'));
-    my $element;
-    if ($Texinfo::Common::root_commands{$command->{'cmdname'}}
-        and $command->{'parent'}
-        and $command->{'parent'}->{'type'}
-        and $command->{'parent'}->{'type'} eq 'element') {
-        $element = $command->{'parent'};
+    my $output_unit;
+    if ($root_commands{$command->{'cmdname'}}) {
+        if ($command->{'associated_unit'}) {
+          $output_unit = $command->{'associated_unit'};
+        } elsif ($command->{'structure'}
+                 and $command->{'structure'}->{'associated_unit'}) {
+          $output_unit = $command->{'structure'}->{'associated_unit'};
+        } elsif ($command->{'parent'}
+                 and $command->{'parent'}->{'type'}
+                 and $command->{'parent'}->{'type'} eq 'element') {
+          $output_unit = $command->{'parent'};
+        }
     }
-    if ($element) {
+
+    if ($output_unit) {
         $result .= &{get_formatting_function($self, 'format_element_header')}($self, $cmdname,
-                                                       $command, $element);
+                                                       $command, $output_unit);
     }
 
     my $heading_level;
     # node is used as heading if there is nothing else.
     if ($cmdname eq 'node') {
-        if (!$element or (!$element->{'extra'}->{'section'}
-            and $element->{'extra'}->{'node'}
-            and $element->{'extra'}->{'node'} eq $command
+        if (!$output_unit or
+            (((!$output_unit->{'extra'}->{'section'}
+              and $output_unit->{'extra'}->{'node'}
+              and $output_unit->{'extra'}->{'node'} eq $command)
+             or
+             ((($output_unit->{'extra'}->{'unit_command'}
+                and $output_unit->{'extra'}->{'unit_command'} eq $command)
+               or
+               ($output_unit->{'unit_command'}
+                and $output_unit->{'unit_command'} eq $command))
+              and $command->{'extra'}
+              and not $command->{'extra'}->{'associated_section'}))
              # bogus node may not have been normalized
             and defined($command->{'extra'}->{'normalized'}))) {
             if ($command->{'extra'}->{'normalized'} eq 'Top') {
@@ -111,7 +142,15 @@ sub ffmpeg_heading_command($$$$$)
             }
         }
     } else {
-        $heading_level = $command->{'level'};
+        if (defined($command->{'extra'})
+            and defined($command->{'extra'}->{'section_level'})) {
+          $heading_level = $command->{'extra'}->{'section_level'};
+        } elsif ($command->{'structure'}
+                 and defined($command->{'structure'}->{'section_level'})) {
+          $heading_level = $command->{'structure'}->{'section_level'};
+        } else {
+          $heading_level = $command->{'level'};
+        }
     }
 
     my $heading = $self->command_text($command);
@@ -119,8 +158,8 @@ sub ffmpeg_heading_command($$$$$)
     # if there is an error in the node.
     if (defined($heading) and $heading ne '' and defined($heading_level)) {
 
-        if ($Texinfo::Common::root_commands{$cmdname}
-            and $Texinfo::Common::sectioning_commands{$cmdname}) {
+        if ($root_commands{$cmdname}
+            and $sectioning_commands{$cmdname}) {
             my $content_href = $self->command_contents_href($command, 'contents',
                                                             $self->{'current_filename'});
             if ($content_href) {
@@ -140,7 +179,13 @@ sub ffmpeg_heading_command($$$$$)
             }
         }
 
-        if ($self->in_preformatted()) {
+        my $in_preformatted;
+        if ($program_version_num >= 7.001090) {
+          $in_preformatted = $self->in_preformatted_context();
+        } else {
+          $in_preformatted = $self->in_preformatted();
+        }
+        if ($in_preformatted) {
             $result .= $heading."\n";
         } else {
             # if the level was changed, set the command name right
@@ -149,21 +194,25 @@ sub ffmpeg_heading_command($$$$$)
                 $cmdname
                     = $Texinfo::Common::level_to_structuring_command{$cmdname}->[$heading_level];
             }
-            # format_heading_text expects an array of headings for texinfo >= 7.0
             if ($program_version_num >= 7.000000) {
-                $heading = [$heading];
-            }
-            $result .= &{get_formatting_function($self,'format_heading_text')}(
+                $result .= &{get_formatting_function($self,'format_heading_text')}($self,
+                     $cmdname, [$cmdname], $heading,
+                     $heading_level +$self->get_conf('CHAPTER_HEADER_LEVEL') -1,
+                     $heading_id, $command);
+
+            } else {
+              $result .= &{get_formatting_function($self,'format_heading_text')}(
                         $self, $cmdname, $heading,
                         $heading_level +
                         $self->get_conf('CHAPTER_HEADER_LEVEL') - 1, $command);
+            }
         }
     }
     $result .= $content if (defined($content));
     return $result;
 }
 
-foreach my $command (keys(%Texinfo::Common::sectioning_commands), 'node') {
+foreach my $command (keys(%sectioning_commands), 'node') {
     texinfo_register_command_formatting($command, \&ffmpeg_heading_command);
 }
 
@@ -188,28 +237,56 @@ sub ffmpeg_begin_file($$$)
     my $filename = shift;
     my $element = shift;
 
-    my $command;
-    if ($element and $self->get_conf('SPLIT')) {
-        $command = $self->element_command($element);
+    my ($element_command, $node_command, $command_for_title);
+    if ($element) {
+        if ($element->{'unit_command'}) {
+          $element_command = $element->{'unit_command'};
+        } elsif ($self->can('tree_unit_element_command')) {
+          $element_command = $self->tree_unit_element_command($element);
+        } elsif ($self->can('tree_unit_element_command')) {
+          $element_command = $self->element_command($element);
+        }
+
+       $node_command = $element_command;
+       if ($element_command and $element_command->{'cmdname'}
+           and $element_command->{'cmdname'} ne 'node'
+           and $element_command->{'extra'}
+           and $element_command->{'extra'}->{'associated_node'}) {
+         $node_command = $element_command->{'extra'}->{'associated_node'};
+       }
+
+       $command_for_title = $element_command if ($self->get_conf('SPLIT'));
     }
 
-    my ($title, $description, $encoding, $date, $css_lines,
-        $doctype, $bodytext, $copying_comment, $after_body_open,
-        $extra_head, $program_and_version, $program_homepage,
+    my ($title, $description, $keywords, $encoding, $date, $css_lines, $doctype,
+        $root_html_element_attributes, $body_attributes, $copying_comment,
+        $after_body_open, $extra_head, $program_and_version, $program_homepage,
         $program, $generator);
-    if ($program_version_num >= 7.000000) {
-        ($title, $description, $encoding, $date, $css_lines,
-         $doctype, $bodytext, $copying_comment, $after_body_open,
+    if ($program_version_num >= 7.001090) {
+        ($title, $description, $keywords, $encoding, $date, $css_lines, $doctype,
+         $root_html_element_attributes, $body_attributes, $copying_comment,
+         $after_body_open, $extra_head, $program_and_version, $program_homepage,
+         $program, $generator) = $self->_file_header_information($command_for_title,
+                                                                 $filename); 
+    } elsif ($program_version_num >= 7.000000) {
+        ($title, $description, $encoding, $date, $css_lines, $doctype,
+         $root_html_element_attributes, $copying_comment, $after_body_open,
          $extra_head, $program_and_version, $program_homepage,
-         $program, $generator) = $self->_file_header_information($command);
+         $program, $generator) = $self->_file_header_information($command_for_title,
+                                                                 $filename);
     } else {
         ($title, $description, $encoding, $date, $css_lines,
-         $doctype, $bodytext, $copying_comment, $after_body_open,
-         $extra_head, $program_and_version, $program_homepage,
-         $program, $generator) = $self->_file_header_informations($command);
+         $doctype, $root_html_element_attributes, $copying_comment,
+         $after_body_open, $extra_head, $program_and_version, $program_homepage,
+         $program, $generator) = $self->_file_header_informations($command_for_title);
     }
 
-    my $links = $self->_get_links ($filename, $element);
+    my $links;
+    if ($program_version_num >= 7.000000) {
+      $links = $self->_get_links($filename, $element, $node_command);
+    } else {
+      $links = $self->_get_links ($filename, $element);
+    }
 
     my $head1 = $ENV{"FFMPEG_HEADER1"} || <<EOT;
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
@@ -252,13 +329,25 @@ sub ffmpeg_program_string($)
   if (defined($self->get_conf('PROGRAM'))
       and $self->get_conf('PROGRAM') ne ''
       and defined($self->get_conf('PACKAGE_URL'))) {
-    return $self->convert_tree(
+    if ($program_version_num >= 7.001090) {
+     return $self->convert_tree(
+      $self->cdt('This document was generated using @uref{{program_homepage}, @emph{{program}}}.',
+         { 'program_homepage' => {'text' => $self->get_conf('PACKAGE_URL')},
+           'program' => {'text' => $self->get_conf('PROGRAM') }}));
+    } else {
+     return $self->convert_tree(
       $self->gdt('This document was generated using @uref{{program_homepage}, @emph{{program}}}.',
-         { 'program_homepage' => $self->get_conf('PACKAGE_URL'),
-           'program' => $self->get_conf('PROGRAM') }));
+         { 'program_homepage' => {'text' => $self->get_conf('PACKAGE_URL')},
+           'program' => {'text' => $self->get_conf('PROGRAM') }}));
+    }
   } else {
-    return $self->convert_tree(
-      $self->gdt('This document was generated automatically.'));
+    if ($program_version_num >= 7.001090) {
+      return $self->convert_tree(
+        $self->cdt('This document was generated automatically.'));
+    } else {
+      return $self->convert_tree(
+        $self->gdt('This document was generated automatically.'));
+    }
   }
 }
 if ($program_version_6_8) {
-- 
2.45.2
