6

I'm currently writing a UPnP remote control app which is used to connect a remote MediaServer to a remote MediaRenderer. Since the actual MP3 files aren't sent to the Android device, I'd like to be able to get the album art of the currently playing file without having to download the entire MP3 file to my phone.

I've read that MediaMetadataRetriever is useful for this kind of thing, but I haven't been able to get it to work. Each way I try it, I keep getting an IllegalArgumentException by the call to MediaMetadataRetriever#setDataSource, which indicates that my file handle or URI is invalid.

MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();

The following works since it's a direct file path on the device itself:

metaRetriever.setDataSource("/sdcard/Music/Daft_Punk/Homework/01 - Daftendirekt.mp3");

However, any of the following fail with the same error:

metaRetriever.setDataSource(appCtx, Uri.parse("http://192.168.1.144:49153/content/media/object_id/94785/res_id/1/rct/aa"));
metaRetriever.setDataSource(appCtx, Uri.parse("http://192.168.1.144:49153/content/media/object_id/94785/res_id/0/ext/file.mp3"));
metaRetriever.setDataSource("http://192.168.1.144:49153/content/media/object_id/94785/res_id/0/ext/file.mp3");

The first one is the albumArtURI pulled from the UPnP metadata (no *.mp3 extension, but the file will download if pasted into a web browser).

The second and third attempts are using the "res" value from the UPnP metadata, which points to the actual file on the server.

I'm hoping I'm just parsing the URI incorrectly, but I'm out of ideas.

Any suggestions? Also, is there a better way to do this entirely when pulling from a UPnP server? FWIW, I'm using the Cling UPnP library.

== SOLUTION ==

I started looking into william-seemann's answer and it led me to this: MediaMetadataRetriever.setDataSource(String path) no longer accepts URLs

Comment #2 on this post mentions using a different version of setDataSource() that still accepts remote URLs.

Here's what I ended up doing and it's working great:

private Bitmap downloadBitmap(final String url) {
     final MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
     metaRetriever.setDataSource(url, new HashMap<String, String>());
     try {
        final byte[] art = metaRetriever.getEmbeddedPicture();
        return BitmapFactory.decodeByteArray(art, 0, art.length);
     } catch (Exception e) {
        Logger.e(LOGTAG, "Couldn't create album art: " + e.getMessage());
        return BitmapFactory.decodeResource(getResources(), R.drawable.album_art_missing);
     }
  }
agelter
  • 331
  • 2
  • 9
  • It turns out you have to pass the HashMap for remote URLS, my tags returned null but at least it stopped crashing with illegalstateException now. – kabuto178 Jan 10 '15 at 03:10

2 Answers2

1

FFmpegMediaMetadataRetriever will extract metadata from a remote file (Disclosure: I created it). I has the same interface as MediaMetadataRetriever but it uses FFmpeg as it's backend. Here is an example:

FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
mmr.setDataSource(mUri);
String album = mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ALBUM);
String artist = mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ARTIST);
byte [] artwork = mmr.getEmbeddedPicture();
mmr.release();
William Seemann
  • 3,440
  • 10
  • 44
  • 78
  • Thanks for pointing me at this! I ended up using _MediaMetadataRetriever_ with a different version of _setDataSource()_. See my solution above. – agelter Aug 01 '13 at 17:30
  • Above class is not working with amazon bucket url, always giving Exception of IllegalstateException. – Silvans Solanki Mar 09 '16 at 07:09
0

Looking at the source code for MediaMetadataRetriever (not from the official Android repo, but it should still be similar, if not equivalent) showed me this:

if (uri == null) {
    throw new IllegalArgumentException();
}

And this:

ContentResolver resolver = context.getContentResolver();

try {
    fd = resolver.openAssetFileDescriptor(uri, "r");
} catch(FileNotFoundException e) {
    throw new IllegalArgumentException();
}

if (fd == null) {
    throw new IllegalArgumentException();
}

FileDescriptor descriptor = fd.getFileDescriptor();

if (!descriptor.valid()) {
    throw new IllegalArgumentException();
}

Your exception is coming from one of those blocks.

From looking at the MediaMetadataRetriever documentation and source code, it seems to me that the file has to be on the device. You can use a Uri, but I think it has to be something like "file:///android_asset/mysound.mp3". I could be wrong though; are you sure that MediaMetadataRetriever can be used to resolve files over a network?

crocboy
  • 2,887
  • 2
  • 22
  • 25
  • No, I'm not sure it can be used over the network. I was hoping it was possible, especially since a URI can be used, but maybe it was only meant for local files. If MediaMetadataRetreiever can't be used for remote files, is there another way to get the album art without downloading the whole file? – agelter Jul 31 '13 at 18:19
  • You could download the album art from an external website (album art "server"), but you would need basic information about the song like title, artist, etc. – crocboy Jul 31 '13 at 19:37