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 23plotframes - Plot video frame sizes using ffprobe and gnuplot 24 25=head1 SYNOPSIS 26 27plotframes [I<options>] [I<input>] 28 29=head1 DESCRIPTION 30 31plotframes reads a multimedia files with ffprobe, and plots the 32collected video sizes with gnuplot. 33 34=head1 OPTIONS 35 36=over 4 37 38=item B<--input|-i> I<infile> 39 40Specify multimedia file to read. This is the file passed to the 41ffprobe command. If not specified it is the first argument passed to 42the script. 43 44=item B<--help|--usage|-h|-?> 45 46Print a brief help message and exit. 47 48=item B<--manpage|-m> 49 50Print the man page. 51 52=item B<--output|-o> I<outfile> 53 54Set the name of the output used by gnuplot. If not specified no output 55is created. Must be used in conjunction with the B<terminal> option. 56 57=item B<--stream|--s> I<stream_specifier> 58 59Specify stream. The value must be a string containing a stream 60specifier. Default value is "v". 61 62=item B<--terminal|-t> I<terminal> 63 64Set 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 66the gnuplot manual for the valid values. 67 68=back 69 70=cut 71 72=head1 SEE ALSO 73 74ffprobe(1), gnuplot(1) 75 76=cut 77 78use warnings; 79use strict; 80 81use File::Temp; 82use JSON -support_by_pp; 83use Getopt::Long; 84use Pod::Usage; 85 86my $input = $ARGV[0]; 87my $stream_specifier = "v"; 88my $gnuplot_terminal = "x11"; 89my $gnuplot_output; 90 91GetOptions ( 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 100die "You must specify an input file\n" unless $input; 101 102# fetch data 103my @cmd = (qw{ffprobe -show_entries frame -select_streams}, $stream_specifier, "-of", "json", $input); 104print STDERR "Executing command: @cmd\n"; 105my $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 117my %stats; 118my $frames = $json_struct->{frames}; 119my $frame_count = 0; 120foreach 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} 132foreach (keys %stats) { close $stats{$_}->{fh}; } 133 134# write gnuplot script 135my %type_color_map = ( 136 "I" => "red", 137 "P" => "green", 138 "B" => "blue" 139 ); 140 141my $gnuplot_script_tmpfile = File::Temp->new(SUFFIX => '.gnuplot'); 142my $fn = $gnuplot_script_tmpfile->filename; 143open(FH, ">", $fn) or die "Couldn't open $fn: $!"; 144print FH << "EOF"; 145set title "video frame sizes" 146set xlabel "frame time" 147set ylabel "frame size (Kbits)" 148set grid 149set terminal "$gnuplot_terminal" 150EOF 151 152print FH "set output \"$gnuplot_output\"\n" if $gnuplot_output; 153print FH "plot"; 154my $sep = ""; 155foreach 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} 161close FH; 162 163# launch gnuplot with the generated script 164system ("gnuplot", "--persist", $gnuplot_script_tmpfile->filename); 165