The script is ported from ffprobe/SourceForge and updated to the current ffprobe version.
Unified · Split
| @@ -0,0 +1,164 @@ | |||
| 1 | #!/usr/bin/env perl | ||
| 2 | |||
| 3 | # Copyright (c) 2007-2013 Stefano Sabatini | ||
| 4 | # | ||
| 5 | # This file is part of FFmpeg. | ||
| 6 | # | ||
| 7 | # FFmpeg is free software; you can redistribute it and/or | ||
| 8 | # modify it under the terms of the GNU Lesser General Public | ||
| 9 | # License as published by the Free Software Foundation; either | ||
| 10 | # version 2.1 of the License, or (at your option) any later version. | ||
| 11 | # | ||
| 12 | # FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 15 | # See the GNU Lesser General Public License for more details. | ||
| 16 | # | ||
| 17 | # You should have received a copy of the GNU Lesser General Public License | ||
| 18 | # along with FFmpeg; if not, write to the Free Software Foundation, Inc., | ||
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | |||
| 21 | =head1 NAME | ||
| 22 | |||
| 23 | plotframes - Plot video frame sizes using ffprobe and gnuplot | ||
| 24 | |||
| 25 | =head1 SYNOPSIS | ||
| 26 | |||
| 27 | plotframes [I<options>] [I<input>] | ||
| 28 | |||
| 29 | =head1 DESCRIPTION | ||
| 30 | |||
| 31 | plotframes reads a multimedia files with ffprobe, and plots the | ||
| 32 | collected video sizes with gnuplot. | ||
| 33 | |||
| 34 | =head1 OPTIONS | ||
| 35 | |||
| 36 | =over 4 | ||
| 37 | |||
| 38 | =item B<--input|-i> I<infile> | ||
| 39 | |||
| 40 | Specify multimedia file to read. This is the file passed to the | ||
| 41 | ffprobe command. If not specified it is the first argument passed to | ||
| 42 | the script. | ||
| 43 | |||
| 44 | =item B<--help|--usage|-h|-?> | ||
| 45 | |||
| 46 | Print a brief help message and exit. | ||
| 47 | |||
| 48 | =item B<--manpage|-m> | ||
| 49 | |||
| 50 | Print the man page. | ||
| 51 | |||
| 52 | =item B<--output|-o> I<outfile> | ||
| 53 | |||
| 54 | Set the name of the output used by gnuplot. If not specified no output | ||
| 55 | is created. Must be used in conjunction with the B<terminal> option. | ||
| 56 | |||
| 57 | =item B<--stream|--s> I<stream_specifier> | ||
| 58 | |||
| 59 | Specify stream. The value must be a string containing a stream | ||
| 60 | specifier. Default value is "v". | ||
| 61 | |||
| 62 | =item B<--terminal|-t> I<terminal> | ||
| 63 | |||
| 64 | Set the name of the terminal used by gnuplot. By default it is | ||
| 65 | "x11". Must be used in conjunction with the B<output> option. Check | ||
| 66 | the gnuplot manual for the valid values. | ||
| 67 | |||
| 68 | =back | ||
| 69 | |||
| 70 | =cut | ||
| 71 | |||
| 72 | =head1 SEE ALSO | ||
| 73 | |||
| 74 | ffprobe(1), gnuplot(1) | ||
| 75 | |||
| 76 | =cut | ||
| 77 | |||
| 78 | use warnings; | ||
| 79 | use strict; | ||
| 80 | |||
| 81 | use File::Temp; | ||
| 82 | use JSON -support_by_pp; | ||
| 83 | use Getopt::Long; | ||
| 84 | use Pod::Usage; | ||
| 85 | |||
| 86 | my $input = $ARGV[0]; | ||
| 87 | my $stream_specifier = "v"; | ||
| 88 | my $gnuplot_terminal = "x11"; | ||
| 89 | my $gnuplot_output; | ||
| 90 | |||
| 91 | GetOptions ( | ||
| 92 | 'input|i=s' => \$input, | ||
| 93 | 'help|usage|?|h' => sub { pod2usage ( { -verbose => 1, -exitval => 0 }) }, | ||
| 94 | 'manpage|m' => sub { pod2usage ( { -verbose => 2, -exitval => 0 }) }, | ||
| 95 | 'stream|s=s' => \$stream_specifier, | ||
| 96 | 'terminal|t=s' => \$gnuplot_terminal, | ||
| 97 | 'output|o=s' => \$gnuplot_output, | ||
| 98 | ) or pod2usage( { -message=> "Parsing error", -verbose => 1, -exitval => 1 }); | ||
| 99 | |||
| 100 | die "You must specify an input file\n" unless $input; | ||
| 101 | |||
| 102 | # fetch data | ||
| 103 | my @cmd = (qw{ffprobe -show_entries frame -select_streams}, $stream_specifier, "-of", "json", $input); | ||
| 104 | print STDERR "Executing command: @cmd\n"; | ||
| 105 | my $json_struct; | ||
| 106 | { | ||
| 107 | open(FH, "-|", @cmd) or die "ffprobe command failed: $!\n"; | ||
| 108 | local $/; | ||
| 109 | my $json_text = <FH>; | ||
| 110 | close FH; | ||
| 111 | die "ffprobe command failed" if $?; | ||
| 112 | eval { $json_struct = decode_json($json_text); }; | ||
| 113 | die "JSON parsing error: $@\n" if $@; | ||
| 114 | } | ||
| 115 | |||
| 116 | # collect and print frame statistics per pict_type | ||
| 117 | my %stats; | ||
| 118 | my $frames = $json_struct->{frames}; | ||
| 119 | my $frame_count = 0; | ||
| 120 | foreach my $frame (@{$frames}) { | ||
| 121 | my $type = $frame->{pict_type}; | ||
| 122 | $frame->{count} = $frame_count++; | ||
| 123 | if (not $stats{$type}) { | ||
| 124 | $stats{$type}->{tmpfile} = File::Temp->new(SUFFIX => '.dat'); | ||
| 125 | my $fn = $stats{$type}->{tmpfile}->filename; | ||
| 126 | open($stats{$type}->{fh}, ">", $fn) or die "Can't open $fn"; | ||
| 127 | } | ||
| 128 | |||
| 129 | print { $stats{$type}->{fh} } | ||
| 130 | "$frame->{count} ", $frame->{pkt_size} * 8 / 1000, "\n"; | ||
| 131 | } | ||
| 132 | foreach (keys %stats) { close $stats{$_}->{fh}; } | ||
| 133 | |||
| 134 | # write gnuplot script | ||
| 135 | my %type_color_map = ( | ||
| 136 | "I" => "red", | ||
| 137 | "P" => "green", | ||
| 138 | "B" => "blue" | ||
| 139 | ); | ||
| 140 | |||
| 141 | my $gnuplot_script_tmpfile = File::Temp->new(SUFFIX => '.gnuplot'); | ||
| 142 | my $fn = $gnuplot_script_tmpfile->filename; | ||
| 143 | open(FH, ">", $fn) or die "Couldn't open $fn: $!"; | ||
| 144 | print FH << "EOF"; | ||
| 145 | set title "video frame sizes" | ||
| 146 | set xlabel "frame time" | ||
| 147 | set ylabel "frame size (Kbits)" | ||
| 148 | set grid | ||
| 149 | set terminal "$gnuplot_terminal" | ||
| 150 | EOF | ||
| 151 | |||
| 152 | print FH "set output \"$gnuplot_output\"\n" if $gnuplot_output; | ||
| 153 | print FH "plot"; | ||
| 154 | my $sep = ""; | ||
| 155 | foreach my $type (keys %stats) { | ||
| 156 | my $fn = $stats{$type}->{tmpfile}->filename; | ||
| 157 | print FH "$sep\"$fn\" title \"$type frames\" with impulses"; | ||
| 158 | print FH " linecolor rgb \"$type_color_map{$type}\"" if $type_color_map{$type}; | ||
| 159 | $sep = ", "; | ||
| 160 | } | ||
| 161 | close FH; | ||
| 162 | |||
| 163 | # launch gnuplot with the generated script | ||
| 164 | system ("gnuplot", "--persist", $gnuplot_script_tmpfile->filename); | ||