2016-09-26 9 views
7

URLソースからmp3ファイルを再生するのに役立つクラスがあります。再生、一時停止、再開時に効果的です。しかし、私は早送りや巻き戻しに混乱しています。再生中にmp3ファイルを早送りまたは巻き戻す方法は?

一時ファイルを使用してMP3データを保存していますが、ユーザーが選択した位置に応じてFileStreamの位置を変更します。しかしそれには問題があります。

問題:まだ位置が存在しない場合。ユーザーが行きたいと毎回が前方または後方ファイルということを意味これはWebRequest.AddRange()を使用して解決することができるが、この場合には、我々は別途バイトを格納するための新しいFileStreamをを開く必要がとAddRange()メソッドを呼び出すenter image description here

を(まだダウンロードしていません)その位置から再ダウンロードされます。ただし、これがあまりにも頻繁に行われた場合は、ファイルを順方向または逆方向にダウンロードする必要があります。

したがって、クォータフレンドリーな単純なソリューションがあれば教えてください。私はそれを行う方法を理解することはできません。助けてください!

マイコード:

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 
} 
+0

あなたは多分、スタンドアロンのVisual Studioソリューションとして、(ユーザーインターフェースを含む)の完全なコードを提供することができますか? – Evk

+0

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

答えて

5

私はそれをやろうだろうか、お見せすることができます - 「waveOut」のバッファがDirectSoundのSecondaryBufferとは全く異なるものではないと仮定。このように振る舞うことができストリームを再生

The way it's meant to be played.

あり出回っダウンロードしたデータがあると、おそらく演奏との間でデータをダウンロードしていません。このダウンロードされたデータを保存するには、追加の情報、つまり\ playorderを追加する必要があります。

簡単にするために、ファイル/ストリームを固定サイズのアトミックサブチャンクで分割します。 100kByte。ファイルが5001 kByte - > 51の場合、サブチャンクが必要です。

ファイルをダウンロードした順に保存し、必要なIDを検索してから、再生バッファ内のサブチャンクをリロードすることができます。

ます。public void AddRange(からint型、int型へ) https://msdn.microsoft.com/de-de/library/7fy67z6d(v=vs.110).aspx

enter image description here

私はあなたが得ることを願って:行うにはあなたがサブチャンクをロードするためにAddRangeのこのバージョンを使用する必要がありますポイント。他の方法との

  • ロードと彼のquequeを補充する必要がある場合playbufferテストをしてみましょう

  • 古いストリームを維持します。

  • サブチャンクがメモリまたはファイルにすべて保存されていない場合にのみダウンロードします。

ファイルに読み込む方法を扱うことができます

File description

+0

解決策は興味深いものです。この回避策では、ユーザーがある位置にジャンプした場合に、チャンクをファイルに書き込む方法は?ダウンロードされたチャンクが '' [1] [2] [3] [4] 'であり、ユーザーが11番目のチャンクに早送りするとします。だから、ファイルを[4] [11] [12] 'のように作ることができます... 4番目と11番目のチャンクの間にチャンクを書きませんか? –

+1

はい、内部の順序がチャンクIDによって記述されるため、可能です。私は少しinfographicで私の答えを更新しました。 –

+0

フロリアン、これは私の問題の素晴らしい解決策でした。私は今この方法で働いており、それは働いています。ありがとうございました。 –

関連する問題