当您运行mplayer时,它将通过stdout显示播放曲目的位置和长度(以及其他一些信息)。
以下是mplayer的输出示例:
MPlayer2 2.0-728-g2c378c7-4+b1 (C) 2000-2012 MPlayer Team
Cannot open file '/home/pi/.mplayer/input.conf': No such file or directory
Failed to open /home/pi/.mplayer/input.conf.
Cannot open file '/etc/mplayer/input.conf': No such file or directory
Failed to open /etc/mplayer/input.conf.
Playing Bomba Estéreo - La Boquilla [Dixone Remix].mp3.
Detected file format: MP2/3 (MPEG audio layer 2/3) (libavformat)
[mp3 @ 0x75bc15b8]max_analyze_duration 5000000 reached
[mp3 @ 0x75bc15b8]Estimating duration from bitrate, this may be inaccurate
[lavf] stream 0: audio (mp3), -aid 0
Clip info:
album_artist: Bomba Estéreo
genre: Latin
title: La Boquilla [Dixone Remix]
artist: Bomba Estéreo
TBPM: 109
TKEY: 11A
album: Unknown
date: 2011
Load subtitles in .
Selected audio codec: MPEG 1.0/2.0/2.5 layers I, II, III [mpg123]
AUDIO: 44100 Hz, 2 ch, s16le, 320.0 kbit/22.68% (ratio: 40000->176400)
AO: [pulse] 44100Hz 2ch s16le (2 bytes per sample)
Video: no video
Starting playback...
A: 47.5 (47.4) of 229.3 (03:49.3) 4.1%
最后一行(A: 47.5 (47.4) of 229.3 (03:49.3) 4.1%
)是我试图读取的内容,但由于某种原因,Process.OutputDataReceived
事件处理程序从未接收到它。
我是不是错过了什么?mplayer是否使用了某种非标准的方式将"A:"行输出到控制台?
以下是代码,以防有帮助:
Public Overrides Sub Play()
player = New Process()
player.EnableRaisingEvents = True
With player.StartInfo
.FileName = "mplayer"
.Arguments = String.Format("-ss {1} -endpos {2} -volume {3} -nolirc -vc null -vo null ""{0}""",
tmpFileName,
mTrack.StartTime,
mTrack.EndTime,
100)
.CreateNoWindow = False
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardError = True
.RedirectStandardInput = True
End With
AddHandler player.OutputDataReceived, AddressOf DataReceived
AddHandler player.ErrorDataReceived, AddressOf DataReceived
AddHandler player.Exited, Sub() KillPlayer()
player.Start()
player.BeginOutputReadLine()
player.BeginErrorReadLine()
waitForPlayer.WaitOne()
KillPlayer()
End Sub
Private Sub DataReceived(sender As Object, e As DataReceivedEventArgs)
If e.Data = Nothing Then Exit Sub
If e.Data.Contains("A: ") Then
' Parse the data
End If
End Sub
显然,唯一的解决方案是在"slave"模式下运行mplayer,如下所述:http://www.mplayerhq.hu/DOCS/tech/slave.txt
在这种模式下,我们可以(通过stdin)向mplayer发送命令,并且响应(如果有的话)将通过stdout发送。
这里有一个非常简单的实现,可以显示mplayer的当前位置(以秒为单位):
using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
namespace TestMplayer {
class MainClass {
private static Process player;
public static void Main(string[] args) {
String fileName = "/home/pi/Documents/Projects/Raspberry/RPiPlayer/RPiPlayer/bin/Electronica/Skrillex - Make It Bun Dem (Damian Marley) [Butch Clancy Remix].mp3";
player = new Process();
player.EnableRaisingEvents = true;
player.StartInfo.FileName = "mplayer";
player.StartInfo.Arguments = String.Format("-slave -nolirc -vc null -vo null "{0}"", fileName);
player.StartInfo.CreateNoWindow = false;
player.StartInfo.UseShellExecute = false;
player.StartInfo.RedirectStandardOutput = true;
player.StartInfo.RedirectStandardError = true;
player.StartInfo.RedirectStandardInput = true;
player.OutputDataReceived += DataReceived;
player.Start();
player.BeginOutputReadLine();
player.BeginErrorReadLine();
Thread getPosThread = new Thread(GetPosLoop);
getPosThread.Start();
}
private static void DataReceived(object o, DataReceivedEventArgs e) {
Console.Clear();
Console.WriteLine(e.Data);
}
private static void GetPosLoop() {
do {
Thread.Sleep(250);
player.StandardInput.Write("get_time_pos" + Environment.NewLine);
} while(!player.HasExited);
}
}
}
我在另一个或多或少以类似方式工作的应用程序(dbPowerAmp)中发现了同样的问题,在我的情况下,问题是进程输出使用Unicode编码来写入stdout缓冲区,因此,我必须将StandardOutputEncoding和StandardError设置为Unicode才能开始阅读。
您的问题似乎是一样的,因为如果在您发布的输出中找不到"A",这清楚地显示了现有的"A"。那么这可能意味着在读取用于读取输出的当前编码时,字符不同。
因此,在读取进程输出时,请尝试设置正确的编码,尝试将其设置为Unicode。
- ProcessStartInfo.StandardOutputEncoding
- ProcessStartInfo.StandardErrorEncoding
使用"读取";而不是";读取线";,并将输入视为二进制,可能会解决您的问题。
首先,是的,mplayer从属模式可能正是您想要的。但是,如果您决定解析控制台输出,这是可能的。
从属模式的存在是有原因的,如果你对在程序中使用mplayer有一半的认真态度,那么花一点时间来弄清楚如何正确使用它是值得的。也就是说,我相信在某些情况下,包装器是合适的方法。也许你想假装mplayer运行正常,并从控制台控制它,但秘密监视文件位置以稍后恢复它?包装器可能比将所有mplayers键盘命令转换为从属模式命令更容易?
你的问题很可能是你试图使用";读线";从内部蟒蛇在一条无尽的线上。输出的那一行包含\r\n而不是行分隔符,因此readline将把它视为一个无休止的行。sed也会以这种方式失败,但其他命令(如grep)在某些情况下\r\n会将其视为。
对的处理不一致,不能依赖。例如,我的grep版本将匹配IF输出视为控制台,并使用分隔输出。但如果输出是一个管道,它会将其视为任何其他字符。
例如:
mplayer TMBG-Older.mp3 2>/dev/null | tr 'r' 'n' | grep "^A: " | sed 's/^A: *([0-9.]*) .*/1/' | tail -n 1
我正在使用";tr〃;此处强制它为'\n',以便管道中的其他命令可以以一致的方式处理它。
此命令管道输出一行,仅包含以秒为单位的结束位置,并带有小数点。但是如果你去掉";tr〃;从这个管道发出命令,坏事就会发生。在我的系统上,它只显示";0.0";作为位置;sed";不能很好地处理'\r'行分隔符,并且所有位置更新都被视为同一行。
我确信python也不能很好地处理\r,这可能是您的问题。如果是,则使用";读取";而不是";读线";将其视为二进制可能是正确的解决方案。
不过,这种方法还有其他问题。缓冲是一个很大的问题^C导致该命令不输出任何内容,mplayer必须优雅地退出以显示任何内容,因为管道缓冲内容,缓冲区在SIGINT上被丢弃。
如果你真的想变得有趣,你可能会把几个输入源放在一起,用多种方式处理输出,然后真正围绕mplayer编写一个包装器。一个脆弱、复杂的包装器,每次更新mplayer、用户做了一些意外的事情,或者正在播放的文件名包含一些奇怪的东西,SIGSTOP或SIGINT时都可能会损坏。也许还有其他我没有想到的事情。