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); | |