Search code examples
androidmedia-playerrandomaccessfile

How can I read from a read-only RandomAccessFile while a MediaPlayer is playing from it in a different position


I have a custom file format (similar to a zip file) that packs small files (small images, mp3 files) into 1 physical file. My android app downloads this file, and it displays one image from it. The user can touch the image and it'll start to play one of the small mp3 "files" inside the packed file. He can also swipe left or right, and the app displays the previous or next image.

In order to make things smoother I am holding 3 "cards" in the memory: the one currently displayed, and the prevous and the next one. This way when it's swiped, I can immediatelly show the next image. In order to do this, I am preloading the images and the mp3 into the MediaPlayer. The problem is that because of this it is multi threaded, as the preloading is done in the background. I have a bug: when I start to play the mp3, and during it's playing I swipe, the image I preaload is cut in the middle. After lots of debugging, I found the reason: while I load the image, the MediaPlayer is moving the file pointer in the file descriptor, and that causes the next read to read from the middle of the mp3 instead of the image.

Here's the code:

InputStream imageStream = myPackedFile.getBaseStream("cat.jpg"); // this returns an InputStream representing "cat.jpg" from my packed file (which is based on a RandomAccessFile)
Drawable image = Drawable.createFromStream(imageStream, imagePath);

FileDescriptor fd = myPackedFile.getFD();
long pos = myPackedFile.getPos("cat.mp3");
long len = myPackedFile.getLength("cat.mp3");
player.setDataSource(fd, pos, len);
player.prepare();

Solution

  • This is what worked for me: Instead of creating a RandomAccessFile and holding to it, I create a File, and every time I need to access it as a RandomAccessFile I create a new one:

    public class PackagedFile {
        private File file;
        PackagedFile(String filename) {
            file = new File(filename);
        }
        public RandomAccessFile getRandomAccessFile() {
            RandomAccessFile raf = null;
            try {
                raf = new RandomAccessFile(file, "r");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return raf;
        }
    }
    

    and the above code became:

    InputStream imageStream = myPackedFile.getBaseStream("cat.jpg"); // this returns an InputStream representing "cat.jpg" from my packed file (which is based on a RandomAccessFile)
    Drawable image = Drawable.createFromStream(imageStream, imagePath);
    
    FileDescriptor fd = myPackedFile.getRandomAccessFile().getFD();
    long pos = myPackedFile.getPos("cat.mp3");
    long len = myPackedFile.getLength("cat.mp3");
    player.setDataSource(fd, pos, len);
    player.prepare();