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