Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Vb.net 通过解析ffprobe输出,在VB表单中填充字段值_Vb.net_Parsing_Video_Text_Ffprobe - Fatal编程技术网

Vb.net 通过解析ffprobe输出,在VB表单中填充字段值

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':

嗯。我试图从视频文件中获取相关媒体信息。为此,我从脚本中运行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':
  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

以下内容将获得与您显示的相同的属性,但将它们放在一个spiffy
ListView
而不是
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()

  • (“streams”)(0)
    引用第0个streams数组元素
  • (“编解码器名称”)
    是该属性的键或名称。大多数值是字符串,但少数是数字
下面的代码(从上面继续)将通过创建对所需特性集的引用来缩短这些引用。正如我所说的,我把它们放在
列表视图中并使用组

' 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