Problem with SDL SDL_RWops "magic" for Android

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: must be included
in the manifest or you can’t use fopen in write mode :wink:

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:

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: must be included in
the manifest or you can’t use fopen in write mode :wink:


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

2012/5/26 John

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.