165 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			165 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | #!/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); |