我有一个课程来帮助我播放来自URL来源的mp3文件。它在播放,暂停和恢复时效果很好。但是我对快进还是落后感到困惑。如何在播放时快进或倒退MP3文件?
我正在使用临时文件来存储MP3数据,我想根据用户选择的位置重新定位FileStream
。但是它有一个问题。
这可以使用WebRequest.AddRange()
得到解决,但在这种情况下,我们要打开一个新的FileStream分别和每一个用户要前进或后退时调用AddRange()
方法存储字节表示该文件将从该位置重新下载。但是,如果这样做太频繁,我们必须下载与向前或向后数量相同的文件。
所以,如果有一个简单和配额友好的解决方案,请让我知道它。我无法弄清楚如何去做。请帮助!
我的代码:
public class NAudioPlayer
{
HttpWebRequest req;
HttpWebResponse resp;
Stream stream;
WaveOut waveOut;
Mp3WaveFormat format;
AcmMp3FrameDecompressor decompressor;
BufferedWaveProvider provider;
FileStream tempFileStream;
System.Windows.Forms.Timer ticker;
private int bufferedDuration;
string url, path;
long size, streamPos;
int timeOffset, timePosition, avgBytes, duration;
bool formatKnown, waitinloop, exitloop;
State currentState;
public NAudioPlayer(string mp3Url)
{
this.url = mp3Url;
this.currentState = State.Stopped;
this.size = -1;
this.timeOffset = 0;
this.timePosition = 0;
this.avgBytes = 0;
this.duration = 0;
this.format = null;
this.ticker = new System.Windows.Forms.Timer();
this.waveOut = new WaveOut();
this.waitinloop = false;
ticker.Interval = 250;
ticker.Tick += ticker_Tick;
}
int target = 0;
void ticker_Tick(object sender, EventArgs e)
{
if (waveOut.PlaybackState == PlaybackState.Playing)
{
timePosition = timeOffset + (int)(waveOut.GetPosition() * 1d/waveOut.OutputWaveFormat.AverageBytesPerSecond);
Debug.WriteLine(timePosition);
}
if (duration != 0 && timePosition >= duration)
{
waveOut.Stop();
ticker.Stop();
}
if (timePosition == target && timePosition < duration - 5 &&
provider != null && provider.BufferedDuration.TotalSeconds < 5)
{
waveOut.Pause();
currentState = State.Buffering;
target = timePosition + 5;
}
if (currentState == State.Buffering && provider != null && provider.BufferedDuration.TotalSeconds >= 5)
{
waveOut.Play();
}
}
public void Play()
{
int range = avgBytes <= 0 ? 0 : timeOffset * avgBytes;
int readBytes = 0;
long pos = 0;
this.streamPos = 0;
exitloop = false;
disposeAllResources();
ticker.Start();
Task.Run(() =>
{
//Crate WebRequest using AddRange to enable repositioning the mp3
req = WebRequest.Create(url) as HttpWebRequest;
req.AllowAutoRedirect = true;
req.ServicePoint.ConnectionLimit = 100;
req.UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0";
req.AddRange(range);
resp = req.GetResponse() as HttpWebResponse;
stream = resp.GetResponseStream();
size = resp.ContentLength;
//Create a unique file to store data
path = Path.GetTempPath() + Guid.NewGuid().ToString() + ".mp3";
tempFileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
waveOut.Stop();
waveOut = new WaveOut();
if (provider != null)
waveOut.Init(provider);
byte[] buffer = new byte[17 * 1024];
while ((readBytes = stream.Read(buffer, 0, buffer.Length)) > 0 ||
timePosition <= duration)
{
while (waitinloop)
Thread.Sleep(500);
if (exitloop)
break;
Mp3Frame frame = null;
tempFileStream.Write(buffer, 0, readBytes);
tempFileStream.Flush();
//Read the stream starting from the point
//where we were at the last reading
using (MemoryStream ms = new MemoryStream(ReadStreamPartially(tempFileStream, streamPos, 1024 * 10)))
{
ms.Position = 0;
try
{
frame = Mp3Frame.LoadFromStream(ms);
}
catch { continue; } //Sometimes it throws Unexpected End of Stream exception
//Couldn't find the problem out, try catch is working for now
if (frame == null)
continue;
pos = ms.Position;
streamPos += pos;
}
if (!formatKnown)
{
format = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2,
frame.FrameLength, frame.BitRate);
duration = (int)(Math.Ceiling(resp.ContentLength * 1d/format.AverageBytesPerSecond));
avgBytes = format.AverageBytesPerSecond;
formatKnown = true;
}
if (decompressor == null)
{
decompressor = new AcmMp3FrameDecompressor(format);
provider = new BufferedWaveProvider(decompressor.OutputFormat);
provider.BufferDuration = TimeSpan.FromSeconds(20);
waveOut.Init(provider);
waveOut.Play();
}
int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
if (IsBufferNearlyFull(provider))
{
Thread.Sleep(500);
}
provider.AddSamples(buffer, 0, decompressed);
}
});
}
void disposeAllResources()
{
if (resp != null)
resp.Close();
if (stream != null)
stream.Close();
if (provider != null)
provider.ClearBuffer();
}
public void Pause()
{
if (waveOut.PlaybackState == PlaybackState.Playing && !waitinloop)
{
waitinloop = true;
waveOut.Pause();
Thread.Sleep(200);
}
}
public void Resume()
{
if (waveOut.PlaybackState == PlaybackState.Paused && waitinloop)
{
waitinloop = false;
waveOut.Play();
Thread.Sleep(200);
}
}
public void ForwardOrBackward(int targetTimePos)
{
waitinloop = false;
exitloop = true;
timeOffset = targetTimePos;
Thread.Sleep(100);
waveOut.Stop();
ticker.Stop();
this.Play();
}
public static byte[] ReadStreamPartially(System.IO.Stream stream, long offset, long count)
{
long originalPosition = 0;
if (stream.CanSeek)
{
originalPosition = stream.Position;
stream.Position = offset;
}
try
{
byte[] readBuffer = new byte[4096];
byte[] total = new byte[count];
int totalBytesRead = 0;
int byteRead;
while ((byteRead = stream.ReadByte()) != -1)
{
Buffer.SetByte(total, totalBytesRead, (byte)byteRead);
totalBytesRead++;
if (totalBytesRead == count)
{
stream.Position = originalPosition;
break;
}
}
if (totalBytesRead < count)
{
byte[] temp = new byte[totalBytesRead];
Buffer.BlockCopy(total, 0, temp, 0, totalBytesRead);
stream.Position = originalPosition;
return temp;
}
return total;
}
finally
{
if (stream.CanSeek)
{
stream.Position = originalPosition;
}
}
}
private bool IsBufferNearlyFull(BufferedWaveProvider bufferedWaveProvider)
{
return bufferedWaveProvider != null &&
bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes
< bufferedWaveProvider.WaveFormat.AverageBytesPerSecond/4;
}
public int Duration
{
get
{
return duration;
}
}
public int TimePosition
{
get
{
return timePosition;
}
}
public int BufferedDuration
{
get { return (int)provider.BufferedDuration.TotalSeconds; }
}
public int TimeOffset
{
get
{
return timeOffset;
}
}
}
public enum State
{
Paused,
Playing,
Stopped,
Buffering
}
你可以提供完整的代码(包括用户界面),可能作为独立的Visual Studio解决方案? – Evk
说实话,我目前正在设计用户界面来创建一个播放栏来显示当前时间并使用户能够快进或快退。但是真的有必要考虑解决方案吗? –