The SDL forums have moved to discourse.libsdl.org.
This is just a read-only archive of the previous forums, to keep old links working.


SDL Forum Index
SDL
Simple Directmedia Layer Forums
music & SDL_RWops "magic" for Android [SOLVED]
wilbefast


Joined: 19 Jul 2011
Posts: 115
Hi all,

Finally gathered up the courage to give music streaming on Android another try. On Android SDL_RWFromFile cleverly reads from the APK archive's assets folder via the JNI. This is great, but for streaming music it's a problem because it's not thread-safe. This shouldn't surprise anyone: streaming data from a zip file, through java, the JNI, SDL, SDL_mixer... it's a recipe for disaster. As a result I'm exporting the files I want to stream to the sdcard first, then streaming them (safely) from there.

At least that's the idea. Trouble is Mixer_LoadMUS itself calls upon SDL_RWFromFile, hence Mixer_LoadMUS("/sdcard/data/music.ogg") calls SDL_RWFromFile("/sdcard/data/music.ogg") which, unless I've very much mis-read the situation, goes looking for "/sdcard/data/music.ogg" in the assets folder of the APP (?). The code that does this trick is line 448 of SDL_rwops.c

#if defined(ANDROID)
    rwops = SDL_AllocRW();
    if (!rwops)
        return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
    if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
        SDL_FreeRW(rwops);
        return NULL;
    }
    rwops->seek = Android_JNI_FileSeek;
    rwops->read = Android_JNI_FileRead;
    rwops->write = Android_JNI_FileWrite;
    rwops->close = Android_JNI_FileClose;


It is called from line 46 of music_ogg.c

/* Load an OGG stream from the given file */
OGG_music *OGG_new(const char *file)
{
    SDL_RWops *rw;

    rw = SDL_RWFromFile(file, "rb");
    if ( rw == NULL ) {
        SDL_SetError("Couldn't open file %s", file);
        return NULL;
    }
    return OGG_new_RW(rw);
}



It would be handy if the JNI file operations were used only for filenames in the working directory, so without the initial '/'. Hence SDL_RWFromFile(const char *file, const char *mode), if Android is defined, could check file[0] == '/' .
I'm not sure how to implement this though: I'd need to tell the RWOps struct that in this case, it should use the standard C fread, fseek, etc operations, which work for Android. Until this is done Mix_LoadMUS isn't going to work. I'm also had some problems with fread(magic, 4, 1, fp) on Android (won't read 4 bytes? why?), though fread(moremagic, 8, 1, fp) works.

Long story short, either I load from the filesystem and mixer redirects the search to the wrong place because of rwops, or I load using rwops from the apk archive, in which case streaming is unsafe and the app crashes after about 5 seconds (potentially longer... if you're Irish and wearing a vest made of 4-leaf clovers).

William

PS - oh, another thing: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> must be included in the manifest or you can't use fopen in write mode Wink
Problem with SDL SDL_RWops "magic" for Android
John
Guest

Avoid the combination of SDL_Mixer, SDL_RWops, and assets on Android if
possible. First problem: SDL seek is implemented as a close(), followed by a
re-open, then repeatedly reading until it hits the offset. The reason has to do
with the assets stream, it's a forward-only, read-only decompression stream.
Second problem: SDL_mixer likes to seek() in certain cases as a kind of `probe`
to see what the stream does. This wreaks havoc on the assets stream or any other
non-trivial stream. Third problem: SDL's seek will mask bad code by clamping to
boundaries. SDL_mixer appears to rely on this clamping, and in some cases will
seek to extreme offsets such ((size_t)-1). Fourth problem: SDL_mixer triggers
some fun reports from valgrind. Fifth problem: it's pretty easy to crash SDL on
Android by touching any of the streams after the app is restored from background
or orientation has changed. If you are targeting platform 10 or above, you can
directly access assets via the direct C api.


On 05/25/2012 07:12 PM, William Dyce wrote:
Quote:
Hi all,

Finally gathered up the courage to give music streaming on Android another try.
On Android SDL_RWFromFile cleverly reads from the APK archive's assets folder
via the JNI. This is great, but for streaming music it's a problem because it's
not thread-safe. This shouldn't surprise anyone: streaming data from a zip file,
through java, the JNI, SDL, SDL_mixer... it's a recipe for disaster. As a result
I'm exporting the files I want to stream to the sdcard first, then streaming
them (safely) from there.

At least that's the idea. Trouble is Mixer_LoadMUS itself calls upon
SDL_RWFromFile, hence Mixer_LoadMUS("/sdcard/data/music.ogg") calls
SDL_RWFromFile("/sdcard/data/music.ogg") which, unless I've very much mis-read
the situation, goes looking for "/sdcard/data/music.ogg" in the assets folder of
the APP (?). The code that does this trick is *line 448 of SDL_rwops.c
*
*#if defined(ANDROID)
rwops = SDL_AllocRW();
if (!rwops)
return NULL; /* SDL_SetError already setup by SDL_AllocRW() */
if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
SDL_FreeRW(rwops);
return NULL;
}
rwops->seek = Android_JNI_FileSeek;
rwops->read = Android_JNI_FileRead;
rwops->write = Android_JNI_FileWrite;
rwops->close = Android_JNI_FileClose;*

It is called from line *46 of music_ogg.c*

*/* Load an OGG stream from the given file */*
*OGG_music *OGG_new(const char *file)*
*{*
* SDL_RWops *rw;*

* rw = SDL_RWFromFile(file, "rb");*
* if ( rw == NULL ) {*
* SDL_SetError("Couldn't open file %s", file);*
* return NULL;*
* }*
* return OGG_new_RW(rw);*
*}*


It would be handy if the JNI file operations were used only for filenames in the
working directory, so without the initial '/'. Hence *SDL_RWFromFile(const char
*file, const char *mode)*, if Android is defined, could check file[0] == '/' .
I'm not sure how to implement this though: I'd need to tell the RWOps struct
that in this case, it should use the standard C fread, fseek, etc operations,
which work for Android. Until this is done *Mix_LoadMUS* isn't going to work.
I'm also had some problems with *fread(magic, 4, 1, fp)* on Android (won't read
4 bytes? why?), though *fread(moremagic, 8, 1, fp)* works.

Long story short, either I load from the filesystem and mixer redirects the
search to the wrong place because of rwops, or I load using rwops from the apk
archive, in which case streaming is unsafe and the app crashes after about 5
seconds (potentially longer... if you're Irish and wearing a vest made of 4-leaf
clovers).

William

PS - oh, another thing: <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> must be included in
the manifest or you can't use fopen in write mode Wink


_______________________________________________
SDL mailing list

http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org
_______________________________________________
SDL mailing list

http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org
Problem with SDL SDL_RWops "magic" for Android
gabomdq


Joined: 28 Jul 2011
Posts: 495
Location: Argentina
2012/5/26 John
Quote:
Avoid the combination of SDL_Mixer, SDL_RWops, and assets on Android if possible. First problem: SDL seek is implemented as a close(), followed by a re-open, then repeatedly reading until it hits the offset. The reason has to do with the assets stream, it's a forward-only, read-only decompression stream.


We actually had a more gracious system but we had to remove it and replace it by the current hack due to an Android bug. http://bugzilla.libsdl.org/show_bug.cgi?id=1301



--
Gabriel.
Re: Problem with SDL SDL_RWops "magic" for Android
wilbefast


Joined: 19 Jul 2011
Posts: 115
John wrote:
Avoid the combination of SDL_Mixer, SDL_RWops, and assets on Android if
possible. First problem: SDL seek is implemented as a close(), followed by a
re-open, then repeatedly reading until it hits the offset. The reason has to do
with the assets stream, it's a forward-only, read-only decompression stream.
Second problem: SDL_mixer likes to seek() in certain cases as a kind of `probe`
to see what the stream does. This wreaks havoc on the assets stream or any other
non-trivial stream. Third problem: SDL's seek will mask bad code by clamping to
boundaries. SDL_mixer appears to rely on this clamping, and in some cases will
seek to extreme offsets such ((size_t)-1). Fourth problem: SDL_mixer triggers
some fun reports from valgrind. Fifth problem: it's pretty easy to crash SDL on
Android by touching any of the streams after the app is restored from background
or orientation has changed. If you are targeting platform 10 or above, you can
directly access assets via the direct C api.

Thanks for the reply - yes, mixer is filthy, I know Wink it's good to know in more depth why it's not unsafe to stream from the APK. Unfortunately my phone isn't version 2.3.3 so I can't use API 10. I came up with a filthy work-around however, which you can find here
In a nutshell:
  1. read the file from the assets folder.
  2. write to the file system.
  3. read it back from the file system.

gabomdq wrote:
We actually had a more gracious system but we had to remove it and replace it by the current hack due to an Android bug. http://bugzilla.libsdl.org/show_bug.cgi?id=1301

Hmm... is this likely to cause me problems now that I'm reading from the filesystem rather than the assets folder?

edit: also, blog post Smile http://wilbefast.com/2012/05/29/android-sound-and-music-via-sdl_rwops/#more-1664