13

i need to download a mp3 file, but i only need the first 20 seconds of the song (or the entire song if the song is less than 20 sec).
this is how i download the entire song:

    func downloadSong(audioUrl: URL) {

    let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)

    URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
        guard let location = location, error == nil else { return }
        do {

            try FileManager.default.moveItem(at: location, to: destinationUrl)
            // song available in destinationUrl

        } catch let error as NSError {

        }
    }).resume()
}

is there a way to stop the download after 20 seconds? i know i can download the entire song and then cut it, but i want to be more efficient, especially if the song is very long.

Eyal
  • 10,777
  • 18
  • 78
  • 130
  • 1
    First off, downloading 20 sec will not make sure that you can play 20 sec. There is no way to determine 100% that you have 20 seconds of audio, you might just want to use an audio streamer and stream the file and stop the stream after 20 seconds. Or provide 20 seconds mp3 files on the server. – rckoenes Mar 15 '17 at 16:36
  • @rckoenes u r right i mean maximum of 20 sec, it could be less – Eyal Mar 15 '17 at 17:22
  • 1
    not possible my bro because first determine information of song and after not sure if 20sec downloaded and mp3 play fine may be possible to missing any frame in 20 sec than not play mp3 – Sunil Prajapati Mar 23 '17 at 04:47

4 Answers4

7

MP3 file is divided into a small blocks - frames. Each frame has constant time length 0.026 sec. But size of one frame (in Bytes) varies according to bitrate. Eg. for 128kbps it is (normally) 417 Bytes and for 192kbps 626 Bytes. The first 4 Bytes of each frame is frame header and the rest is audio data.

Quoted from here.

And each frame is playable. So for 20 seconds of data, you need approximately 770 frames(20/0.026). So download first 770 frames then cancel the download task. Then play your downloaded frames.

Or by size, download first 313.5 KB (Calculation is: (417 Bytes*770)/1024) of data for 128kbps file. And you will get your 20 seconds of data.

Partho Biswas
  • 2,290
  • 1
  • 24
  • 39
  • I think you should also answer on how to download that first `313.5 KB` . – Arpit Jain Mar 21 '17 at 11:03
  • still didn't have the chance to check this, but this is the most detailed and practical answer so far, i will update later on the result. thanks – Eyal Mar 25 '17 at 18:46
2

You would have to cut the audio file to 20 seconds, then you could download it.

  • yes, definitively the way to go. Moreover: all ID tags.should be stripped out. This is by far the most quick & easy way to do such a task, by using a server-side batch script. All other solutions would require to read the mp3 file until the length of all sound frames has been reached 20 secs. – deblocker Mar 25 '17 at 11:24
2

I have also faced such challenge in one of my application. After, searching a lot I finally concluded it is not possible. As downloading an mp3 file has its own internal algorithm which works according to frames. So, we cannot predict anything regarding how much seconds or minutes of audio we have downloaded.

Further, you can refer this link.

Download last 30 seconds of an mp3

Community
  • 1
  • 1
Arpit Jain
  • 1,660
  • 12
  • 23
1

From my opinion this is definitely possible with only few assumptions:

  • Server should support Content-Range header
  • you need some knowledge (or good library) in internal mp3 structure

The idea:

Download 500 kb from server by requesting this count in range. Then you have two option:

  1. fast-and-dirty - cross fingers and supply this data directly to player and see what happens:)
  2. Supply received data to mp3-aware library (or manually parse header). Patch file length inside metadata and then try to play it this time for sure
sage444
  • 5,661
  • 4
  • 33
  • 60