3
votes

I have been working for a multi-platform desktop application that need to implement both mp3 and ogg player including seeking, Now my mp3 part is Ok, but seeking in ogg play is not working...

I am using BasicPlayer of javazoom.jlgui and following is my seek function

/**
 * Skip bytes in the File inputstream. It will skip N frames matching to
 * bytes, so it will never skip given bytes length exactly.
 *
 * @param bytes
 * @return value>0 for File and value=0 for URL and InputStream
 * @throws BasicPlayerException
 * @throws IOException
 */
protected long skipBytes(long bytes) throws BasicPlayerException, NullPointerException, IOException {

    long totalSkipped = 0;

    if (m_dataSource instanceof File) {
        log.info("Bytes to skip : " + bytes);
        int previousStatus = m_status;
        m_status = SEEKING;
        long skipped = 0;

        synchronized (m_audioInputStream) {
            notifyEvent(BasicPlayerEvent.SEEKING, getEncodedStreamPosition(), -1, null);
            initAudioInputStream();

            if (m_audioInputStream != null) {
                // Loop until bytes are really skipped.
                while (totalSkipped < (bytes - SKIP_INACCURACY_SIZE)) {
                    skipped = m_audioInputStream.skip(bytes - totalSkipped);
                    if (skipped == 0) {
                        break;
                    }

                    totalSkipped = totalSkipped + skipped;
                    log.info("Skipped : " + totalSkipped + "/" + bytes);

                    if (totalSkipped == -1) {
                        throw new BasicPlayerException(BasicPlayerException.SKIPNOTSUPPORTED);
                    }
                }
            }
        }

        notifyEvent(BasicPlayerEvent.SEEKED, getEncodedStreamPosition(), -1, null);
        m_status = OPENED;

        if (previousStatus == PLAYING) {
            startPlayback();
        } else if (previousStatus == PAUSED) {
            startPlayback();
            pausePlayback();
        }
    }

    return totalSkipped;
}

is there any way to add another seek function to implement seek for ogg? thanks in advance...

Don't know much about how this works,but it works fine for mp3 including for seeking, audio play,pause,stop In ogg playing,pause,stop are fine, but seeking is the problem, when seeked audio playing starts from the beginning.

I didn't change any part of the code source of BasicPlayer Api is http://www.javazoom.net/jlgui/sources/basicplayer3.0.zip

when I checked details in depth, jlgui3.0 player implemented using this code is also not implemented seeking for Ogg play, they have mentioned it. So the current code needs to be added with a separate seek function for Ogg playing and use it instead of current seek(), whenever the playing audio is Ogg...

I don't know how to do so, help me with good source to refer and easy way to implement the same...

1
"seeking in ogg play is not working" What's it doing? Or not doing? Also else if (previousStatus == PAUSED) { startPlayback(); pausePlayback(); }? (I may be ignorant of how your program works but start -> immediately pause seems suspicious.) - Radiodef
@Radiodef: Question edited... - Fahad
I don't think this is going to be a simple, straightforward task to implement seeking. The OGG format is very different, and the underlying support for seeking does not seem correct. - helmy
OGG.. Better to try decoding the entire track to PCM first then play/seek through that instead. All audio decoders must convert from compressed formats to PCM before you can hear it anyway so why not seek the PCM before sending to speakers? Anyways for OGG seeking you would have to travel through the bytes of your file to find the start of OGG page (x4F\x67\x67\x53), then you go 6 bytes ahead and from 7 to 14th byte you read Granule Pos (8 Bytes). Now the complication is: WHAT'S AUDIO CODEC? Flac, Opus, Vorbis? GranulePos could mean how many PCM samples decoded so far (samples = time) - VC.One
Yes it's possible but not ideal for large sizes (30mb would use around 318mb from how many gigs of ram?). some apps write to hard drive a temp file instead. But I hear your concern and will try provide a solution soon. I dont use Java so its a problem to advise about this properly. Can you handle bytes though? Meaning find and read from position? and also can you write value(s) to some new byte array? This is because in some systems to seek means to extract the remaining portion of bytes from seeked pos and feed to OGG decoder. Each seek becomes a new "grab" from original file bytes - VC.One

1 Answers

1
votes

I think this library may not be sufficient for your needs. Looking at the source code of jlGui, (pasted below) seeking is only supported for .mp3 files and .wav files.

Moreover, the logic behind it is somewhat naive, because it only hops forward by a fixed number of bytes. For PCM format .wav files, the number of bytes skipped will always be proportional to the amount of time skipped, but for variable bit rate .mp3 files, the amount of time you skip will vary with the instantaneous bitrate at different points in the file. Ogg Vorbis is a container format that supports multiplexing, which makes seeking more complicated than just jumping ahead in the file and resynchronizing the decoder.

As an alternative, I would recommend using the JavaSound or JavaFX APIs, which both can play audio files and seek to a time you provide, rather than a byte offset.

From jlGui:

protected void processSeek(double rate)
{
    try
    {
        if ((audioInfo != null) && (audioInfo.containsKey("audio.type")))
        {
            String type = (String) audioInfo.get("audio.type");
            // Seek support for MP3.
            if ((type.equalsIgnoreCase("mp3")) && (audioInfo.containsKey("audio.length.bytes")))
            {
                long skipBytes = (long) Math.round(((Integer) audioInfo.get("audio.length.bytes")).intValue() * rate);
                log.debug("Seek value (MP3) : " + skipBytes);
                theSoundPlayer.seek(skipBytes);
            }
            // Seek support for WAV.
            else if ((type.equalsIgnoreCase("wave")) && (audioInfo.containsKey("audio.length.bytes")))
            {
                long skipBytes = (long) Math.round(((Integer) audioInfo.get("audio.length.bytes")).intValue() * rate);
                log.debug("Seek value (WAVE) : " + skipBytes);
                theSoundPlayer.seek(skipBytes);
            }
            else posValueJump = false;
        }
        else posValueJump = false;
    }
    catch (BasicPlayerException ioe)
    {
        log.error("Cannot skip", ioe);
        posValueJump = false;
    }
}