Log in

View Full Version : Win software for detailed info on MKV/h264?


shae
6th September 2014, 23:37
Can anyone suggest a good software to show as many details as possible on MKV/h264 files? Something akin to a combination of GSpot and MP4Modifier.

MediaInfo doesn't do a full scan so can't give info on frame type and QP distribution. It also doesn't give accurate info on audio streams, such as DTS-HD.

raffriff42
7th September 2014, 00:48
Some related discussion here: http://forum.doom9.org/showthread.php?t=167170

shae
7th September 2014, 11:42
Thanks.

That thread's about something more in depth, but even that suggests there's no such tool. Surprising.

vid.user
7th September 2014, 14:25
ffprobe -show_frames -i video.ext (checkout the help for a load of other stuff too).

LoRd_MuldeR
7th September 2014, 14:38
Thanks.

That thread's about something more in depth, but even that suggests there's no such tool. Surprising.

Well, most MediaInfo-like tools provide information on the container level. So they can give you information that can be derived from the container file's header, like which streams are available in the file and what the type of each stream is. They may also be able to give you some more information about a stream's properties, as long as that info can be gathered in a straight-forward manner (e.g. by looking at the stream's "private" header data). However, detailed information, like frame type and QP distribution, would require to actually parse and (at least partly) decode the entire stream! Not only would this by slow, it would also require that the Info tool implements a fully-fledged decoder for all relevant audio and video formats...

(FFmpeg/FFprobe is probably the closets to this)

shae
7th September 2014, 23:56
-show_frames... I'll check it out. The other thread seemed to suggest ffprobe doesn't provide all the info, but maybe it does more than I expected.

I know it would require parsing. I was only after h264/MKV (well, maybe MP4 wouldn't hurt as well), so similar to MP4Modifier's MPEG4 ASP/AVI support.

Why decode, to figure out QP?

LoRd_MuldeR
8th September 2014, 01:25
Why decode, to figure out QP?

A video file contains entropy coded (http://en.wikipedia.org/wiki/Entropy_encoding) bistream. In H.264, for example, CABAC or CAVLC is used for that purpose. So even if you want only the "global" QP for each frame, you will need to decode the bistream at least to the point where you have each frame header in uncompressed form to read the QP value, I suppose. Since H.264 supports adaptive quantization, you will even need to further decode each frame, in order to get the QP offset for each individual macro block...

raffriff42
8th September 2014, 06:09
ffplay.exe -hide_banner -an -debug 1 -loop 1 -autoexit -i "video.mp4" 2> "info.txt"...gives you:. . .
[h264 @ 013cc9a0] slice:1 F mb:0 B fix pps:0 frame:2 poc:65664/65664 ref:3/1 qp:25 loop:1:-2:-2 weight:2 SPAT
1.98 M-V: 0.005 fd= 0 aq= 0KB vq= 3247KB sq= 0B f=0/30
[h264 @ 00c7b240] slice:1 F mb:0 P fix pps:0 frame:2 poc:65674/65674 ref:5/1 qp:26 loop:1:-2:-2 weight:1
2.01 M-V: 0.003 fd= 0 aq= 0KB vq= 3199KB sq= 0B f=0/30
[h264 @ 013cc460] slice:1 F mb:0 B fix pps:0 frame:3 poc:65670/65670 ref:4/1 qp:26 loop:1:-2:-2 weight:2 SPAT
2.04 M-V: 0.001 fd= 0 aq= 0KB vq= 3155KB sq= 0B f=0/31
[h264 @ 013cc9a0] slice:1 F mb:0 B fix pps:0 frame:4 poc:65668/65668 ref:2/2 qp:26 loop:1:-2:-2 weight:2 SPAT
2.09 M-V: 0.014 fd= 0 aq= 0KB vq= 3120KB sq= 0B f=0/31
[h264 @ 00c7b240] slice:1 F mb:0 B fix pps:0 frame:4 poc:65672/65672 ref:3/1 qp:26 loop:1:-2:-2 weight:2 SPAT
2.12 M-V: 0.012 fd= 0 aq= 0KB vq= 3086KB sq= 0B f=0/32
[h264 @ 013cc460] slice:1 F mb:0 P fix pps:0 frame:4 poc:65682/65682 ref:5/1 qp:27 loop:1:-2:-2 weight:1
2.15 M-V: 0.010 fd= 0 aq= 0KB vq= 3040KB sq= 0B f=0/32
. . .
...etc etc. Hard to read most of it, but qp per frame is there. When run, a video window opens (the -nodisp switch did not work for me), and you have to let play through to the end in real time.

shae
8th September 2014, 16:59
Conclusion: GUI needed. :)

Multiple lines from ffplay for some of the frames (different "poc" though). Are these different slices despite them all saying "slice:1"?

Emulgator
9th September 2014, 21:06
GUI: h264visa / CodecVisa

raffriff42
10th September 2014, 04:11
Parsing the raw log and writing CSV files:
https://www.dropbox.com/s/wzqxccwboidv0vl/FFInfo42-spreads3.jpg?raw=1

For ease of importing into spreadsheets, values expressed in the log as "numerator/denominator" are split into 2 columns (eg, faults_n, faults_d), and "loop" values, ie "1:-1:-1" are split into 3 columns - spreadsheets tend to interpret such strings as dates or formulas. Likewise, unit suffixes like "10KB" are put in their own columns.

The translation is not complete - each codec (or other module within ffmpeg) logs its own format, and each needs to have a translator written for it. I have got translators for ffplay's default log format, and for the H264 decoder. I'm seeing a pattern that should make it easier to add more parsers in the future. My code is written in C# with that idea in mind.

What I don't know right now is how the frames relate between the several CSV files. The frames are *probably* logged in decode order (same as bitstream order?), but one or more may be in presentation order instead - or some other order.

...If I'm gonna do much more on this, seeing as how CodecVisa gets $2K and up, I think I need to be paid :)

Download (https://www.dropbox.com/s/fye8g8l69vknt6s/FFInfo42-01.zip?dl=0) (master BAT file, Console mini-app, source code)
(temporary Dropbox location - might make a SourceForge project in future, I don't know)
Usage:
1) edit BAT, set path to executables.
2) drop a video file on the BAT file.
3) Video will play all the way through.
4) When done, one or more CSV files will be placed in video folder.

BAT file: @echo off

::** CONFIGURATION SECTION *******
set FPLAY="C:\Program Files\ffmpeg\bin\ffplay.exe"
set FPROB="C:\Program Files\ffmpeg\bin\ffprobe.exe"
set INF42="%~dp0\FFInfo42.exe"
::** END CONFIGURATION SECTION ***

if not exist %FPLAY% @echo ffplay.exe not found
if not exist %FPLAY% pause
if not exist %FPLAY% goto :EOF

@echo.
@echo *** ffplay dumping info to file ***
@echo.
ver > nul
@echo on
%FPLAY% -an -debug 1 -loop 1 -autoexit -i "%~dpnx1" 2> "%~dpnx1-ffplay-debug.txt"
@echo off
if errorlevel 1 pause
if errorlevel 1 goto :EOF

if not exist %FPROB% @echo ffprobe.exe not found
if not exist %FPROB% pause
if not exist %FPROB% goto :EOF

@echo.
@echo *** ffprobe dumping info to CSV file ***
@echo.
@echo entry,media_type,key_frame,pkt_pts,pkt_pts_time,pkt_dts,pkt_dts_time,best_effort_timestamp,^
best_effort_timestamp_time,pkt_duration,pkt_duration_time,pkt_pos,pkt_size,^
width,height,pix_fmt,sample_aspect_ratio,pict_type,coded_picture_number,^
display_picture_number,interlaced_frame,top_field_first,repeat_pict > "%~dpn1-ffprobe.csv"
ver > nul
@echo on
%FPROB% -select_streams v -print_format csv -show_entries frame "%~dpnx1" >> "%~dpn1-ffprobe.csv"
@echo off
if errorlevel 1 pause
if errorlevel 1 goto :EOF

if not exist "%~dpnx1-ffplay-debug.txt" goto :EOF
if not exist %INF42% @echo FFInfo42 not found
if not exist %INF42% pause
if not exist %INF42% goto :EOF

@echo on
%INF42% "%~dpnx1-ffplay-debug.txt"
@echo off
if errorlevel 1 pause
goto :EOF



Main source file (_FFInfo42-Main.cs), without project files etc, posted below.using System;
using io = System.IO;
using col = System.Collections.Generic;
using rex = System.Text.RegularExpressions;
using txt = System.Text;

// This library 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.
//
// This library 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.

namespace FFInfo42
{
////////////////////////////////////////////////////////////////
abstract class BaseTranslator
{
protected io.StreamWriter _writer = null;
protected rex.Regex _re = null;
protected String _header;
protected String _lastError = "";
protected String _srcPath = null;
protected String _outPath = null;
protected int _outLineNo = -1;

/// <summary> message for most recent error, if any.
/// </summary>
public String lastError { get { return _lastError; } }

/// <summary> assign value to member _srcPath; reset _outLineNo.
/// </summary>
/// <param name="srcPath"> path of original source; create a similarly named output file.
/// </param>
public virtual bool Init(String srcPath) {
_srcPath = srcPath;
_outLineNo = 0;
return true;
}

/// <summary> release resources & close output file.
/// </summary>
public virtual void Close() {
try {
_writer.Close();
_writer = null;
}
catch (Exception e) {
_lastError = e.Message; // (ignore)
}
}

protected String sprintf2regex(String s) {
return s.Replace("%c", @"\S")
.Replace("%s", @"\S*")
.Replace("%u", @"\d+")
.Replace("%d", @"-?\d+")
.Replace("%f", @"-?\d+\.\d+|nan");
}

/// <summary> try to match line against private regex; if match, write transformed result to output file.
/// * if this is first success, create an output file.
/// </summary>
/// <param name="sLine"> one line from source to be interpreted
/// </param>
/// <returns> true on success, false on failure (if false, check lastError)
/// </returns>
public abstract bool processLine(int lineNo, String sLine);

}

////////////////////////////////////////////////////////////////
class H264Translator : BaseTranslator
{
public override bool Init(String srcPath)
{
if (! base.Init(srcPath)) {
return false;
}
_outPath = srcPath + "-H264.csv";

_header = @"slice_num,frame_type,mb,pict_type,fix,IDR,pps,frame_num,poc_n,poc_d," +
@"ref_n,ref_d,qp,loop1,loop2,loop3,weight,weight_c,slice_type";
try {
_re = new rex.Regex(sprintf2regex(
@"^\[.+] slice:(%d) (F|T|B) mb:(%d) (%c)( fix)?( IDR)? pps:(%u) frame:(%d) poc:(%d)/(%d)" +
@" ref:(%d)/(%d) qp:(%d) loop:(%d):(%d):(%d) +weight:(%d)(c?)( \S+)?.*"));
return true;
}
catch(Exception e) {
_lastError = "Can't init H264Translator: " + e.Message;
return false;
}
}

public override bool processLine(int lineNo, String sLine)
{
try {
if (! _re.IsMatch(sLine)) {
return true;
}
if (_outLineNo <= 0)
{
try {
_writer = new io.StreamWriter(_outPath, false, txt.Encoding.ASCII);
_writer.WriteLine(_header);
_outLineNo++;
}
catch (Exception e2) {
_lastError = String.Format("H264Translator: can't open '{0}' for writing: {1}",
_outPath, e2.Message);
return false;
}
}
String s2 = _re.Replace(sLine,
@"\1,\2,\3,\4,\5,\6,\7,\8,\9,\10,\11,\12,\13,\14,\15,\16,\17,\18,\19".Replace("\\", "$"));
_writer.WriteLine(s2);
_outLineNo++;
return true;
}
catch (Exception e) {
_lastError = String.Format("H264Translator: error(1) at line {0}: {1}",
lineNo, e.Message);
return false;
}
}

public override void Close() {
try {
_writer.WriteLine();
base.Close();
}
catch (Exception e) {
_lastError = e.Message; // (ignore)
}
}
}

////////////////////////////////////////////////////////////////
class FFPlayTranslator : BaseTranslator
{
public override bool Init(String srcPath)
{
if (! base.Init(srcPath)) {
return false;
}
_outPath = srcPath + "-FFPlay.csv";

_header = @"clock,av_st,av_diff,frame_drops,aq_size,aqkb," +
@"vq_size,vqkb,sq_size,sqkb,faults_n,faults_d";
try {
_re = new rex.Regex(sprintf2regex(
@"^[ \t]*(%f) +(%s): *(%f) +fd= *(%d) +aq= *(%d *)(K?B)" +
@" +vq= *(%d *)(K?B) +sq= *(%d *)(B) +f=(%d)/(%d)"));
return true;
}
catch (Exception e) {
_lastError = "Can't init FFPlayTranslator: " + e.Message;
return false;
}
}

public override bool processLine(int lineNo, String sLine)
{
try {
if (! _re.IsMatch(sLine)) {
return true;
}
if (_outLineNo <= 0) {
try {
_writer = new io.StreamWriter(_outPath, false, txt.Encoding.ASCII);
_writer.WriteLine(_header);
_outLineNo++;
}
catch (Exception e2) {
_lastError = String.Format("FFPlayTranslator: can't open '{0}' for writing: {1}",
_outPath, e2.Message);
return false;
}
}
String s2 = _re.Replace(sLine,
@"\1,\2,\3,\4,\5,\6,\7,\8,\9,\10,\11,\12".Replace("\\", "$"));
_writer.WriteLine(s2);
_outLineNo++;
return true;
}
catch (Exception e)
{
_lastError = String.Format("FFPlayTranslator: error(1) at line {0}: {1}",
lineNo, e.Message);
return false;
}
}

public override void Close() {
try {
_writer.WriteLine();
base.Close();
}
catch (Exception e) {
_lastError = e.Message; // (ignore)
}
}
}

////////////////////////////////////////////////////////////////
class FFInfoMain
{
static void Main(string[] args)
{
Console.WriteLine("FFInfo42: read output from 'ffplay.exe -debug 1' \n" +
"and write a CSV file suitable for spreadsheets, etc \n" +
"version 0.42.01 9-Sep-2014 raffriff42");
try {
if (args.Length==0) {
Console.WriteLine("USAGE: FFInfo42 <path to ffplay log file>");
System.Environment.Exit(1);
}
String arg = args[0];
if (! io.File.Exists(arg)) {
Console.WriteLine("can't find file '" + arg + "'");
System.Environment.Exit(1);
}

BaseTranslator[] x = {
new H264Translator(),
new FFPlayTranslator(),
};
for (int i=0; i<x.Length; i++) {
if (! x[i].Init(arg) ) {
Console.WriteLine(x[i].lastError);
x[i] = null;
}
}

int srcLineNo = 0;
using (io.StreamReader sr = new io.StreamReader(arg, txt.Encoding.ASCII))
{
String sLine;
while ((sLine = sr.ReadLine()) != null)
{
srcLineNo++;
for (int i=0; i<x.Length; i++) {
if (x[i] !=null) {
if (! x[i].processLine(srcLineNo, sLine) ) {
Console.WriteLine(x[i].lastError);
x[i] = null;
}
}
}
}
}
Console.WriteLine("processing complete.");
System.Environment.Exit(0);
}
catch (Exception e) {
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
System.Environment.Exit(1);
}
}
}
}

StainlessS
11th September 2014, 14:42
...If I'm gonna do much more on this, seeing as how CodecVisa gets $2K and up, I think I need to be paid :)


Dont be a greedy guts RaffRiff, you've already won some software.

Win software for detailed info on MKV/h264.

raffriff42
12th September 2014, 03:35
Haha, true, although it's not very good ATM, and even if it was, I probably wouldn't use it - ffprobe alone is good enough for me.
However, it will be more useful if I can merge the data into a single table, and I will work on that as time allows.

raffriff42
14th September 2014, 04:50
Still working on my program, but ran across 2 interesting alternatives:
(EDIT I have put my project on indefinite hold as there is no need for it; alt. #2 works great)

1.
Get the JM Reference H264 Codec (http://iphome.hhi.de/suehring/tml/download/).
* Build it (I used VC++ 2008 Express; had to disable compiler option "OpenMP" to squash a linker error)
* Edit bin/decode.cfg to specify your input path (unmuxed, .264 streams only)
* run the decoder, redirecting standard output to a file. You get something like this:----------------------------- JM 18.6 (FRExt) -----------------------------
POC must = frame# or field# for SNRs to be correct
--------------------------------------------------------------------------
Frame POC Pic# QP SnrY SnrU SnrV Y:U:V Time(ms)
--------------------------------------------------------------------------
00000(IDR) 0 0 26 4:2:0 36
00003( P ) 6 1 26 4:2:0 25
00002( B ) 4 2 26 4:2:0 20
00001( b ) 2 3 26 4:2:0 19
00006( P ) 12 3 26 4:2:0 23
00005( B ) 10 4 26 4:2:0 56
00004( b ) 8 5 26 4:2:0 40
00009( P ) 18 5 26 4:2:0 65
00008( B ) 16 6 26 4:2:0 22
00007( b ) 14 7 26 4:2:0 42
00012( P ) 24 7 26 4:2:0 65
00011( B ) 22 8 26 4:2:0 23
00010( b ) 20 9 26 4:2:0 70
00014( P ) 28 9 26 4:2:0 43
00013( b ) 26 10 26 4:2:0 43
00000(IDR) 0 0 23 4:2:0 179
00003( P ) 6 1 26 4:2:0 26
. . ....etc. If I had supplied a "reference" clip, I would have gotten SNR stats too, which is neat.

The output can be imported into a spreadsheet in fixed width mode - but some hand editing will be needed.

I tried a couple of other decoders, but this had the best analysis dump. Which ain't saying much for the others.

2.
ffdshow tryouts (http://ffdshow-tryout.sourceforge.net/)'s OSD feature.
* Start menu, Video decoder configuration
* OSD section; Enable the following items:Decoder, Output description, Current frame, Frame timestamps,
Raw frame timestamps, Frame type, Input bitrate,
Coded frame size, Frame mean quantizer, Accurate deblocking.* Check "Save To" and specify a path
* Open the file in VirtualDub, manually selecting "Directshow input driver" under "Files of type."
** EDIT This does not work in VirtualDub (http://forum.doom9.org/showthread.php?p=1738245#post1738245) for me any more (2015)
** VirtualDub not required; any media player set up to use ffdshow filters will work Depending on your font size & color settings, you should see something like this:
https://www.dropbox.com/s/nv16ab1con032fb/ffdshow-OSD-2-x264.jpg?raw=1
* Select File, Run video analysis pass. (Run video analysis pass ensures no frames are skipped)
* When complete, Close video.
* Open the file path you specified previously. You will get something like this:;Decoder: ;Output description: ;Current frame: ;Frame timestamps: ;Raw frame timestam
;libavcodec h264;RGB24, flipped vertically;0;00:00:00.000 - 00:00:00.033;0 - 333666;I
;libavcodec h264;RGB24, flipped vertically;1;00:00:00.033 - 00:00:00.066;333666 - 667
;libavcodec h264;RGB24, flipped vertically;2;00:00:00.066 - 00:00:00.100;667333 - 100
;libavcodec h264;RGB24, flipped vertically;3;00:00:00.100 - 00:00:00.133;1001000 - 13
;libavcodec h264;RGB24, flipped vertically;4;00:00:00.133 - 00:00:00.166;1334666 - 16
;libavcodec h264;RGB24, flipped vertically;5;00:00:00.166 - 00:00:00.200;1668333 - 20
;libavcodec h264;RGB24, flipped vertically;6;00:00:00.200 - 00:00:00.233;2002000 - 23
;libavcodec h264;RGB24, flipped vertically;7;00:00:00.233 - 00:00:00.266;2335666 - 26
;libavcodec h264;RGB24, flipped vertically;8;00:00:00.266 - 00:00:00.300;2669333 - 30
;libavcodec h264;RGB24, flipped vertically;9;00:00:00.300 - 00:00:00.333;3003000 - 33
;libavcodec h264;RGB24, flipped vertically;10;00:00:00.333 - 00:00:00.367;3336666 - 3
;libavcodec h264;RGB24, flipped vertically;11;00:00:00.367 - 00:00:00.400;3670333 - 4
. . .(edited for width to fit on a forum page)
It can be imported into a spreadsheet by specifying ';' (semicolon) as the field delimiter.

This method is awesome because it works with any video format supported by ffdshow.

Of course, saving to file is not needed if the on-screen readout is all you need.

shae
5th October 2014, 00:55
GUI: h264visa / CodecVisa
Thanks. I'll see if the trial is enough. :)

raffriff: Thanks! So C# is your go-to thing for text processing? (I'm more prone to Javascript, possibly under WSH.)

raffriff42
5th October 2014, 03:50
Thanks, no haha, that was my first C# project evar, but C# is very much like Visual Basic with curly braces, so it was an easy transition. Yes scripting would have been perfectly sufficient, but at the time I thought the project might grow, get a GUI, etc.

juliusfriedman
14th October 2014, 18:07
I have recently added support for Matroska in my library @ https://net7mma.codeplex.com/.

You will be able to extract anything you need from a MKV using the library and within the next two weeks it will be a complete muxing solution for any supported container.

Currently supported formats are:

Base Media,
Matroska,
RIFF,
ASF /WMV
Material Exchange (OMF / AAF)

It reliably retrieves all tracks and their correct information such as Track Type, Duration, Created, Modified, Frame Rate, Channel Count, Height and Width.

Additionally it can retrieve some meta data from files where it is included such as Authors or Rating.

It also includes a Rtp Client and Rtsp Client as well as a RtspServer with archiving support.

It achieves all this in under 375k and is written in completely Managed C#.

Let me know if you find it useful or can suggest any API Improvements or find any files in which it does not work with.:thanks:

albesp77
15th January 2016, 15:00
first sorry to all if i take back this old post, personally i'm using ffdshow-tryout with virtualdub without problems (settings with codec tweak), i obtain csv file and i can see quantizer values and calculate myself all that i need, this is really good, also because virtualdub (run video analysis pass) run smootly and can obtain csv with not much time, instead ffplay take a lot of time and for myself (i don't know if is my error) i see only first 15 frames loop back in debug window. now i want to see if there is another possible solution to obtain quantizer by frame, also because i see that ffdshow-tryout is discontinued, any help ? is not possible to use ffplay with much speed in output without showing video ? or similar ? thanks to any reply!