22

I'm using Android's MediaPlayer to play MP3 files that are simultaneously downloaded from the internet using the DownloadManager. I'm aware of the MediaPlayer's capability to stream directly, but I don't want to use that:

The obvious advantage you get by downloading and simultaneously streaming from file is that this way, the file gets stored and is available locally afterwards.

  • This works in principle - the MediaPlayer starts to play the file even if it not fully downloaded. (BTW my problem is NOT that I can't get MediaPlayer to play.)
  • The problem is that when the MediaPlayer gets to the position where the download status just was when it started playing, it stops and calls onCompletion. (Clarification: Let's assume the file is 12% downloaded when I start playing. When the player gets to the position at 12%, it stops.)
  • Quick side note: OnBufferingUpdateListener.onBufferingUpdate(...) is not called when something is appended to the MP3 file via the DownloadManager.

So the question is: How can I simultaneously download an audio file to the file system and play it? (Android 4+ suffices, if that makes a difference.)

Hinton
  • 2,320
  • 4
  • 26
  • 32
  • How are you doing the streaming? By implementing a simple localhost server that reads data from the downloading file and feeds it to a MediaPlayer that has been set to stream from localhost? In that case, couldn't you make your server return a Content-Length that matches or exceeds the full length of the file? – Michael Dec 13 '12 at 18:17
  • Well, I just point the MediaPlayer to the file currently and set its mode to STREAM_BLAH (whatever it's actually called). So I don't have a server currently. – Hinton Dec 14 '12 at 10:06
  • Hi Hinton, I have to do the same thing you are asking can you post an answer(with some detail) how you did it so that I can implement it on my side. – Ashwin N Bhanushali Mar 29 '13 at 05:37
  • I'm quite busy at the moment but I'll try to answer in a few days if you still want it after you've read this. Problem: The thing I implemented at last is quite ugly. I tried Oren's approach (which is obviously the best, and correct solution), but it wasn't too important to me so I just decided to stream from the URL by default and let the user tell me if they wanted to download. In the latter case, I'd stream from the URL while downloading (yes: waste of bandwidth), and when the download was finished, I'd switch from streaming to the downloaded file when the user would pause. – Hinton Mar 29 '13 at 10:55
  • Continued: This works remarkably well. The problem is the wasted bandwidth. If you're doing this for a customer who is not very attentive and not knowledgeable about android programming, they'll never notice. It works at least as a temporary solution - customer telling you "i WANT this feature" - then it's obviously better to implement ANYthing quickly and try to fix it when you've got time (= "never", if you're a cynical person). – Hinton Mar 29 '13 at 10:57

3 Answers3

9

I assume you're pointing MediaPlayer to the file. In that case I don't think it supports streaming, and it probably checks the size of the file and such in advance, so it won't update itself as more of the file comes in.

You have to point it to a content source that will work with streaming, e.g. HTTP. I've just checked how "ES File Explorer" does it (Using LogCat) - when you click on a video file in a remote file share, it opens an local HTTP server, and sends to the MediaPlayer an HTTP Uri to its local server, that also includes the remote path.

When it gets the request through HTTP, the local server starts reading the file from the remote location, and providing it locally to MediaPlayer through HTTP. You can probably do the same, and in addition to providing it via HTTP also save it to disk.

Oren
  • 1,796
  • 1
  • 15
  • 17
  • 1
    Hi Oren can you tell how to create local http server and how we can use it with mediaplayer? – Ashwin N Bhanushali Mar 29 '13 at 05:37
  • 1
    I use [https://github.com/NanoHttpd/nanohttpd], which is great for a lightweight server. Additional options here: [http://stackoverflow.com/questions/6329468/create-http-server-android] – Oren May 01 '13 at 18:34
  • I am looking for the same solution. Can you elaborate what you have done to achieve this? @Oren – Kannan_SJD Jun 10 '16 at 06:34
5

It helped me, I hope to help someone else.

Player download & play music.

mMediaPlayer.setDataSource(DOWNLOAD_URL);

   File root = Environment.getExternalStorageDirectory();
   File dir = new File(root + "/Folder");
   if (!dir.exists()) {
        dir.mkdirs();

DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
            Uri downloadUri = Uri.parse(DOWNLOAD_URL);
            DownloadManager.Request request = new DownloadManager.Request(downloadUri);
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
            request.setAllowedOverRoaming(false);
            request.setTitle(TITLE);
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC, TITLE);
            request.allowScanningByMediaScanner();

After downloading file:

mMediaPlayer.setDataSource(Environment.getExternalStorageDirectory() + "/Folder/" + TITLE + ".mp3");
Nikita G.
  • 720
  • 1
  • 14
  • 22
  • Does this code download the file twice? Once via the mediaplayer and second via the downloadmanager? Or is the downloadmanager able to fetch the file from the mediaplayer? – ibyte Mar 27 '20 at 11:32
1

I have the same problem. Of course, When I use setDataSource(video_uri) and aCustomDownload(video_uri) at the same time, it can save and play, but it wasted bandwidth.

I tried to make a fake blocking FileDescriptor(FileInputStream, LocalSocket) to setDataSource, and other ways, but all of them were failed.

So I found this solution, VideoCache.

  • It via a http proxy server, same as @Oren 's answer. It builds a socket local http server , Then listening for a valid url like http://127.0.0.1:xxxx/url_encode('your video url').

  • When it gets a valid url, it creates a socket request client to download the url, and send the stream to server of 127.0.0.1. SO you can setDataSource('http://127.0.0.1:xxxx/your video's encode url')

  • Use once download, save and play. And it can made a singleton server with one more Activity(lifecycle) lauching, so I can use it from android.intent.action.SEND or other LAUNCHER.

It's a prefect solution.

Notice: you must turn off the cache limit. otherwise VideoCache will clear all data when cache size was 512MB(default).

周宏成
  • 175
  • 1
  • 1
  • 8