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
正直言って、私は現在、現在の時間を表示し、早送りまたは巻き戻しを可能にするプレーヤーバーを作成するためのUIを設計中です。しかし、ソリューションについて考えるのは本当に必要ですか? –