yading@11: #!/usr/bin/env perl yading@11: yading@11: # Copyright (c) 2007-2013 Stefano Sabatini yading@11: # yading@11: # This file is part of FFmpeg. yading@11: # yading@11: # FFmpeg is free software; you can redistribute it and/or yading@11: # modify it under the terms of the GNU Lesser General Public yading@11: # License as published by the Free Software Foundation; either yading@11: # version 2.1 of the License, or (at your option) any later version. yading@11: # yading@11: # FFmpeg is distributed in the hope that it will be useful, yading@11: # but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. yading@11: # See the GNU Lesser General Public License for more details. yading@11: # yading@11: # You should have received a copy of the GNU Lesser General Public License yading@11: # along with FFmpeg; if not, write to the Free Software Foundation, Inc., yading@11: # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: yading@11: =head1 NAME yading@11: yading@11: plotframes - Plot video frame sizes using ffprobe and gnuplot yading@11: yading@11: =head1 SYNOPSIS yading@11: yading@11: plotframes [I] [I] yading@11: yading@11: =head1 DESCRIPTION yading@11: yading@11: plotframes reads a multimedia files with ffprobe, and plots the yading@11: collected video sizes with gnuplot. yading@11: yading@11: =head1 OPTIONS yading@11: yading@11: =over 4 yading@11: yading@11: =item B<--input|-i> I yading@11: yading@11: Specify multimedia file to read. This is the file passed to the yading@11: ffprobe command. If not specified it is the first argument passed to yading@11: the script. yading@11: yading@11: =item B<--help|--usage|-h|-?> yading@11: yading@11: Print a brief help message and exit. yading@11: yading@11: =item B<--manpage|-m> yading@11: yading@11: Print the man page. yading@11: yading@11: =item B<--output|-o> I yading@11: yading@11: Set the name of the output used by gnuplot. If not specified no output yading@11: is created. Must be used in conjunction with the B option. yading@11: yading@11: =item B<--stream|--s> I yading@11: yading@11: Specify stream. The value must be a string containing a stream yading@11: specifier. Default value is "v". yading@11: yading@11: =item B<--terminal|-t> I yading@11: yading@11: Set the name of the terminal used by gnuplot. By default it is yading@11: "x11". Must be used in conjunction with the B option. Check yading@11: the gnuplot manual for the valid values. yading@11: yading@11: =back yading@11: yading@11: =cut yading@11: yading@11: =head1 SEE ALSO yading@11: yading@11: ffprobe(1), gnuplot(1) yading@11: yading@11: =cut yading@11: yading@11: use warnings; yading@11: use strict; yading@11: yading@11: use File::Temp; yading@11: use JSON -support_by_pp; yading@11: use Getopt::Long; yading@11: use Pod::Usage; yading@11: yading@11: my $input = $ARGV[0]; yading@11: my $stream_specifier = "v"; yading@11: my $gnuplot_terminal = "x11"; yading@11: my $gnuplot_output; yading@11: yading@11: GetOptions ( yading@11: 'input|i=s' => \$input, yading@11: 'help|usage|?|h' => sub { pod2usage ( { -verbose => 1, -exitval => 0 }) }, yading@11: 'manpage|m' => sub { pod2usage ( { -verbose => 2, -exitval => 0 }) }, yading@11: 'stream|s=s' => \$stream_specifier, yading@11: 'terminal|t=s' => \$gnuplot_terminal, yading@11: 'output|o=s' => \$gnuplot_output, yading@11: ) or pod2usage( { -message=> "Parsing error", -verbose => 1, -exitval => 1 }); yading@11: yading@11: die "You must specify an input file\n" unless $input; yading@11: yading@11: # fetch data yading@11: my @cmd = (qw{ffprobe -show_entries frame -select_streams}, $stream_specifier, "-of", "json", $input); yading@11: print STDERR "Executing command: @cmd\n"; yading@11: my $json_struct; yading@11: { yading@11: open(FH, "-|", @cmd) or die "ffprobe command failed: $!\n"; yading@11: local $/; yading@11: my $json_text = ; yading@11: close FH; yading@11: die "ffprobe command failed" if $?; yading@11: eval { $json_struct = decode_json($json_text); }; yading@11: die "JSON parsing error: $@\n" if $@; yading@11: } yading@11: yading@11: # collect and print frame statistics per pict_type yading@11: my %stats; yading@11: my $frames = $json_struct->{frames}; yading@11: my $frame_count = 0; yading@11: foreach my $frame (@{$frames}) { yading@11: my $type = $frame->{pict_type}; yading@11: $frame->{count} = $frame_count++; yading@11: if (not $stats{$type}) { yading@11: $stats{$type}->{tmpfile} = File::Temp->new(SUFFIX => '.dat'); yading@11: my $fn = $stats{$type}->{tmpfile}->filename; yading@11: open($stats{$type}->{fh}, ">", $fn) or die "Can't open $fn"; yading@11: } yading@11: yading@11: print { $stats{$type}->{fh} } yading@11: "$frame->{count} ", $frame->{pkt_size} * 8 / 1000, "\n"; yading@11: } yading@11: foreach (keys %stats) { close $stats{$_}->{fh}; } yading@11: yading@11: # write gnuplot script yading@11: my %type_color_map = ( yading@11: "I" => "red", yading@11: "P" => "green", yading@11: "B" => "blue" yading@11: ); yading@11: yading@11: my $gnuplot_script_tmpfile = File::Temp->new(SUFFIX => '.gnuplot'); yading@11: my $fn = $gnuplot_script_tmpfile->filename; yading@11: open(FH, ">", $fn) or die "Couldn't open $fn: $!"; yading@11: print FH << "EOF"; yading@11: set title "video frame sizes" yading@11: set xlabel "frame time" yading@11: set ylabel "frame size (Kbits)" yading@11: set grid yading@11: set terminal "$gnuplot_terminal" yading@11: EOF yading@11: yading@11: print FH "set output \"$gnuplot_output\"\n" if $gnuplot_output; yading@11: print FH "plot"; yading@11: my $sep = ""; yading@11: foreach my $type (keys %stats) { yading@11: my $fn = $stats{$type}->{tmpfile}->filename; yading@11: print FH "$sep\"$fn\" title \"$type frames\" with impulses"; yading@11: print FH " linecolor rgb \"$type_color_map{$type}\"" if $type_color_map{$type}; yading@11: $sep = ", "; yading@11: } yading@11: close FH; yading@11: yading@11: # launch gnuplot with the generated script yading@11: system ("gnuplot", "--persist", $gnuplot_script_tmpfile->filename);