Vb.net 通过解析ffprobe输出,在VB表单中填充字段值
嗯。我试图从视频文件中获取相关媒体信息。为此,我从脚本中运行ffprobe。大概是这样的:Vb.net 通过解析ffprobe输出,在VB表单中填充字段值,vb.net,parsing,video,text,ffprobe,Vb.net,Parsing,Video,Text,Ffprobe,嗯。我试图从视频文件中获取相关媒体信息。为此,我从脚本中运行ffprobe。大概是这样的: Shell("cmd /c [ffpath]\ffprobe.exe -probesize 1000000 -hide_banner -i ""[path]\[video].mp4"" >""[path]\[video]_probe.log"" 2>&1") Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '[path]\[video].mp4':
Shell("cmd /c [ffpath]\ffprobe.exe -probesize 1000000 -hide_banner -i ""[path]\[video].mp4"" >""[path]\[video]_probe.log"" 2>&1")
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '[path]\[video].mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.8.102
Duration: 00:04:34.41, start: 0.033333, bitrate: 957 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x480 [SAR 32:27 DAR 16:9], 820 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
这将创建一个包含以下内容的文件:
Shell("cmd /c [ffpath]\ffprobe.exe -probesize 1000000 -hide_banner -i ""[path]\[video].mp4"" >""[path]\[video]_probe.log"" 2>&1")
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '[path]\[video].mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.8.102
Duration: 00:04:34.41, start: 0.033333, bitrate: 957 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x480 [SAR 32:27 DAR 16:9], 820 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
我说“类似这样的东西”,因为流#0:0(und):行上的数据并不总是以相同的顺序排列。我需要从日志中获取以下信息:
- 持续时间(00:04:34.41)
- 编解码器(h264)
- 分辨率(720x480)
- 宽度(从x之前的分辨率开始)
- 高度(从x后的分辨率开始)
- 帧速率(29.97 fps)
- 音频编解码器(aac)
- 音频采样频率(48 KHz)
- 音频质量(192 kb/s)
REM Find frame rate and resolution
set count=0
for /f "tokens=1-18* delims=," %%a in (input.log) do (
REM we only need to parse the first line
if !count!==0 (
set fps=%%e
echo !fps! >res.tmp
findstr "fps" res.tmp >nul
if not !errorlevel!==0 (
set fps=%%f
)
set resolution=%%c
echo !resolution! >res.tmp
findstr "x" res.tmp >nul
rem echo errorlevel = !errorlevel!
if not !errorlevel!==0 (
set resolution=%%d
)
del null
set fps=!fps:~1,-4!
)
set /A count+=1
)
如何在VB中实现这一点?我正在将Visual Studio Express 2015用于桌面
好的,在做了更多的挖掘和游戏之后,下面是我如何完成任务的:
Sub main()
Dim strDuration As String
Dim strCodec As String
Dim strRes As String
Dim lngWidth, lngHeight As Long
Dim strAudCodec As String
Dim dblFPS As Double
Dim audFreq As Double
Dim audQual As Double
Using logReader As New Microsoft.VisualBasic.FileIO.TextFieldParser("..\..\Sirach and Matthew003_probe.log")
logReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
logReader.SetDelimiters(",")
Dim curRow As String()
While Not logReader.EndOfData
Try
curRow = logReader.ReadFields()
'look for and assign duration
If Mid(curRow(0), 1, 8) = "Duration" Then
strDuration = Mid(curRow(0), 11, 11)
End If
'look for the video stream row
If Mid(curRow(0), 19, 6) = "Video:" Then
'Assign the video codec
strCodec = Mid(curRow(0), 26, Len(curRow(0)))
strCodec = Mid(strCodec, 1, InStr(1, strCodec, " ", CompareMethod.Text) - 1)
'Look in each field of current row
For i = 0 To 10 Step 1
'look for the field containing the resolution ("x" should be the 4th or 5th character)
If InStr(1, curRow(i), "x", CompareMethod.Text) = 4 Or InStr(1, curRow(i), "x", CompareMethod.Text) = 5 Then
'Assign resolution
strRes = Mid(curRow(i), 1, InStr(1, curRow(i), " ", CompareMethod.Text))
'Assign Width
lngWidth = Mid(strRes, 1, InStr(1, strRes, "x", CompareMethod.Text) - 1)
'Assign Heigh
lngHeight = Mid(strRes, InStr(1, strRes, "x", CompareMethod.Text) + 1, Len(strRes))
End If
'loof for fps suffix
If Mid(curRow(i), Len(curRow(i)) - 2, 3) = "fps" Then
'Assign frame rate
dblFPS = Mid(curRow(i), 1, Len(curRow(i)) - 4)
End If
Next i
End If
'Look for the audio stream row
If Mid(curRow(0), 19, 6) = "Audio:" Then
'Assign the audio codec
strAudCodec = Mid(curRow(0), 26, Len(curRow(0)))
strAudCodec = Mid(strAudCodec, 1, InStr(1, strAudCodec, " ", CompareMethod.Text) - 1)
For i = 0 To 10 Step 1
'look for the field containing the audio sampling frequency
If InStr(1, curRow(i), "Hz", CompareMethod.Text) Then
'Assign Audio Sampling Frequency
audFreq = Mid(curRow(i), 1, InStr(1, curRow(i), " ", CompareMethod.Text) - 1)
End If
'look for the field containing the audio quality
If InStr(1, curRow(i), "kb/s", CompareMethod.Text) Then
'assign audio quality
audQual = Mid(curRow(i), 1, InStr(1, curRow(i), " ", CompareMethod.Text) - 1)
End If
Next
End If
Catch ex As Exception
End Try
End While
End Using
Dim strMsg As String
strMsg = "Duration: " & strDuration & Chr(13) _
& "Codec: " & strCodec & Chr(13) _
& "Resolution: " & strRes & Chr(13) _
& "Width: " & lngWidth & Chr(13) _
& "Height: " & lngHeight & Chr(13) _
& "Frame Rate: " & dblFPS & " fps" & Chr(13) _
& "Audio Codec: " & strAudCodec & Chr(13) _
& "Audio Sampling Freq: " & audFreq & " Hz" & Chr(13) _
& "Audio Quality: " & audQual & " kb/s" & Chr(13)
MsgBox(strMsg)
End Sub
结果如下:
我现在需要做的就是使用表单代码中的变量填充表单的字段:Me.[field].text=[variable]
,这样应该很好
如您所见,我利用curRow()
数组,使用mid()
和InStr()
函数缩小字段范围。虽然它似乎已经完成了任务,但我不确定这是最好的方法,还是有其他方法
如果您有任何改进建议,请告诉我。
如果您有任何改进建议,请告诉我。
有一种更直接的方式来获取信息,而不是使用旧的Shell
命令,还有一种稍微有条理的方式来访问信息。以下内容将直接从标准输出读取ffprobe
的结果。为此,请使用全新的Net进程
类,而不是旧的Shell
FFProbe还支持json输出(我认为还有xml),这将允许您查询JObject
以通过“name”取回参数。此命令行参数为-print\u format json
以下内容将获得与您显示的相同的属性,但将它们放在一个spiffyListView
而不是MsgBox
中。由于编码的原因,引用可能有点长,但我将演示如何缩短它们。最重要的是,没有更直接的字符串解析。您将需要:
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
'...
' the part to run ffprobe and read the result to a string:
Dim Quote = Convert.ToChar(34)
Dim json As String
Using p As New Process
p.StartInfo.FileName = "...ffprobe.exe" ' use your path
p.StartInfo.Arguments = String.Format(" -v quiet -print_format json -show_streams {0}{1}{0}",
Quote, theFileName)
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.CreateNoWindow = True
p.StartInfo.UseShellExecute = False
p.Start()
json = p.StandardOutput.ReadToEnd()
End Using
生成的json如下所示(部分显示!):
“streams”是一个4元素数组;显然,视频是元素0,音频是元素1。我不知道另外两个是什么。其中每一个都可以反序列化到表示媒体属性的名称-值对字典中
解析后,视频编解码器将是:myJObj(“流”)(0)(“编解码器名称”)。ToString()
:
引用第0个streams数组元素(“streams”)(0)
是该属性的键或名称。大多数值是字符串,但少数是数字(“编解码器名称”)
列表视图中并使用组
' parse to a temp JObject
Dim jobj= JObject.Parse(json)
' get a list/array of dictionary of prop name/value pairs
Dim medprops = JsonConvert.DeserializeObject( _
Of List(Of Dictionary(Of String, Object)))(jobj("streams").ToString)
' get Video list:
Dim Props = medprops(0)
AddNewLVItem("Codec", Props("codec_name").ToString, "Video")
Dim secs As Double = Convert.ToDouble(Props("duration").ToString)
Dim ts = TimeSpan.FromSeconds(secs)
AddNewLVItem("Duration", ts.ToString("hh\:mm\:ss"), "Video")
AddNewLVItem("Fr Width", Props("width").ToString, "Video")
AddNewLVItem("Fr Height", Props("height").ToString, "Video")
' get avg fr rate text
Dim afr = Props("avg_frame_rate").ToString
Dim AvgFrRate As String = "???"
' split on "/"
If afr.Contains("/") Then
Dim split = afr.Split("/"c)
' calc by dividing (0) by (1)
AvgFrRate = (Convert.ToDouble(split(0)) / Convert.ToDouble(split(1))).ToString
End If
AddNewLVItem("Avg Frame Rate", AvgFrRate, "Video")
' NB: audio stream values come from element (1):
Props = medprops(1)
AddNewLVItem("Audio Codec", Props("codec_name").ToString, "Audio")
AddNewLVItem("Audio Sample Rate", Props("sample_rate").ToString, "Audio")
' avg bit rate is apparently decimal (1000) not metric (1024)
Dim abr As Integer = Convert.ToInt32(Props("bit_rate").ToString)
abr = Convert.ToInt32(abr / 1000)
AddNewLVItem("Audio Bit Rate", abr.ToString, "Audio")
几乎所有内容都以字符串形式返回,许多内容需要执行一些计算或格式化。例如,平均帧速率返回为“2997/100”
,因此您可以看到我将其拆分、转换为整数并进行除法的位置。此外,音频比特率似乎是十进制的,而不是公制的,可能类似于127997
您将能够在调试器中看到各种键和值,以定位其他值。您还可以粘贴生成的json字符串,以便更好地理解结构和读取键
LV助手很简单:
Private Sub AddNewLVItem(text As String, value As String, g As String)
Dim LVI = New ListViewItem
LVI.Text = text
LVI.SubItems.Add(value)
LVI.Group = myLV.Groups(g)
myLV.Items.Add(LVI)
End Sub
引用可以是密集的,也可以是“冗长的”,但它似乎远没有切碎字符串那么乏味和复杂。结果是:
我发现MediaInfo.dll经常无法返回帧速率,并且几乎总是提供不准确的帧数
和什么相比?我对6个文件进行了快速测试,MediaInfo匹配了Explorer和ffprobe。它也有多个条目,而且CBR/VBR存在一些问题。另外两种获取媒体信息的方法是a)从资源管理器;它有一些基本的东西b)使用MediaInfo DLL;它提供了一切:@puropoix我发现MediaInfo.dll经常无法返回帧速率,并且几乎总是提供不准确的帧数。我想您可能有一点输入错误。Dim mp=JObject.Parse(json)
应该是Dim jobj=JObject.Parse(json)
,或者jobj(“流”)。ToString
应该是mp(“流”)。ToString
任何一种更正都允许脚本运行。我发现以这种方式使用ffprobe的结果好坏参半。它适用于某些媒体文件,而不适用于其他媒体文件。例如,某些媒体文件在其流中没有持续时间标记,但持续时间在json输出开头的无标题部分中可用。关于如何把握这个持续时间有什么想法吗?是的,我认为它最初有太多类似命名的变量(mp和medprops),我改变了一个以防止混淆。如果在参数中添加-show_format
,则可以使用jobj(“format”)(“duration”)
对其进行解析。我不知道你在说什么没有标题的部分,因为里面(没有格式)都是流道具的集合。看来你又一次帮我摆脱了困境。使用您的示例,我能够创建我的的New()
sub