[Vorbis-dev] Thread Problem.

Ryan Sullivan rsullivan at islandofficials.com
Sat May 26 20:54:56 PDT 2012


I have been working on a game for a while now and noticed that using your
library to stream music created a graphical problem while the screen would
scroll. (certain tiles would flicker to be more specific). I found that if
I don't play music this problem doesn't occur. Another developer suggested
that if I can lower the priority (thread priority) of the music this could
fix the problem. Before hearing about this the music was not running off
it's own separate thread so the only way I could do this was by creating
it's own thread to stream the music, and then the rest of the code runs
within the main thread. I realized that with the code being changed to use
it's own thread it gets rid of the screen flickering completely. The
problem is I can't seem to get it to not crash the game. After looking at
this for a while I don't believe it to be a thread stack overflow that
occurs. It seems the code is not thread safe and I've tried a bunch of
things with mutexes or changes to the code but have not had success with
this so far. The crash will either occur in ov_read or at random points
within the main thread of the game. (such as doing change animation calls
or cases where the game won't crash if music isn't using it's own thread)
I'm going to send basically what I hope to be the only relevant code for
the music and see if anyone has any suggestions on what I can do to fix
this problem.

-Note: I can't exactly send all the music code, but if you need to see more
let me know.

To start it off the main function for the music thread looks like this:

void ReadStrmData()
{
        // Switch buffer page
        char *buf;
        if(strm.bufPage == 0)
        {
            buf = strm_buf;
            strm.bufPage = 1;
        }
        else
        {
            buf = strm_buf + /*STRM_BUF_PAGESIZE*/4096;
            strm.bufPage = 0;
        }

        long total_bytes_read = 0;

        while(total_bytes_read < /*STRM_BUF_PAGESIZE*/4096)
        {
            int dummy;
            long bytes_read = ov_read(
                &strm.ovf,
                &buf[total_bytes_read],
                /*STRM_BUF_PAGESIZE*/4096 - total_bytes_read,
                &dummy);

            if(bytes_read < 0)
            {
                Printf("\nov_read error");
            }
            else if(bytes_read == 0)
            {
                // End of data
                if(!music_queue.empty())
                {
                    ov_clear(&strm.ovf);

                    // Get next song
                    strm.raw_ogg = music_queue[0];
                    music_queue.pop_front();
                    strm.pos = 0;

                    if(strm.raw_ogg != NULL)
                    {
                        if(ov_open_callbacks(&strm, &strm.ovf, NULL, 0,
my_callbacks) < 0)
                        {
                            panic("ov_open_callbacks failed - 2\n");
                        }
                    }
                    else
                    {
                        // NULL pointer signifies silence
                        StopMusic();
                        return;
                    }
                }
                else
                {
                    // Loop
                    ov_pcm_seek(&strm.ovf, 0);
                }
            }
            else
            {
                total_bytes_read += bytes_read;
            }
        }
    }


-To explain what each variable is, the first variable in that function is
strm. It's a StreamInfo structure, which looks like this:

struct StreamInfo
{
        OggVorbis_File ovf;
        RawOggData *raw_ogg;
        int pos;
        u32 bufPage;
        bool is_playing;
};

-At the moment strm is just a global variable. I've tried a few other
things with this such as having ReadStrmData take a StreamInfo pointer.
This has been recommended since the global variable is also referenced in
the main thread at points when the first song would load or the same way
when a new song is loaded into a level. I am also going to show you the
whole process of loading a song as well. The thing is often times I've
noticed the crash still occur even when a change from one song to another
does not occur. The music thread consists only of ReadStrmData calls so the
only time strm would be being used at all while music isn't changing is
within the music thread. That was just something I attempted but I don't
believe that changed anything in terms of where the crash occurs.

-strm_buf looks like this:

char strm_buf[STRM_BUF_SIZE * 2] ATTRIBUTE_ALIGN(32);

-Again it's another global variable.

-music_queue looks like this:

std::deque<RawOggData *> music_queue;

-Another global variable.

I'm now going to show the process of loading a song, storing it in that
last mentioned deque and then how it differs when music is it's own thread
and not it's own thread.

The load song function looks something like this:

void LoadSong(std::string name_, std::deque<RawOggData> &raw_oggs)
{
     g_music_fade = 16;

    KillStream();
    music_queue.clear();
    raw_oggs.clear();

    //then something like this
    //this is really a condition here which depends on
   //the name variable parameter
  //the important part is that it loads an intro to a song
  //and then a looped portion of a song
   addAndQueueSegment("song_intro", raw_oggs);
   addAndQueueSegment("song_theme_loop", raw_oggs);

    PlayStream();

}

void addAndQueueSegment(const char *name, std::deque<RawOggData> &raw_oggs)
    {
        std::string filename;
        filename += name;
        filename += ".ogg";
        raw_oggs.resize(raw_oggs.size()+1);
        RawOggData &last = *(raw_oggs.end()-1);
        loadFromPakfileIntoVector<char>(filename.c_str(), last);
        QueueMusicSegment(last);
    }

void QueueMusicSegment(RawOggData &raw_ogg)
{
    music_queue.push_back(&raw_ogg);
}


void KillStream()
    {
        if(strm.is_playing)
        {
            stopMusicImpl(); //this function just stops the music
            ov_clear(&strm.ovf);
        }
    }

    void PlayStream()
    {
        // Stops if during playback
        if(strm.is_playing)
        {
            KillStream();
        }

        strm.raw_ogg = music_queue[0];
        music_queue.pop_front();
        strm.pos = 0;

        if(ov_open_callbacks(&strm, &strm.ovf, NULL, 0, my_callbacks) < 0)
        {
            Printf("\nov_open_callbacks failed - 1");
        }

        strm.is_playing = TRUE;

        /* Set parameters */
        g_timer_value = SND_TIMER_CLOCK / ov_info(&strm.ovf, -1)->rate;
        u32 alarmPeriod = g_timer_value * STRM_BUF_PAGESIZE / 32U;
        alarmPeriod /= sizeof(s16);

        //-----

        // Reads initial stream data
        strm.bufPage = 0;
        ReadStrmData();
        ReadStrmData();     // Need to do it twice to fill the buffer

        //----

         //code here that sets up an alarm, which calls a function called
SoundAlarmHandler

    }


    void SoundAlarmHandler(void *arg)
    {
        //note in the case where a separate thread is not created for the
music, ReadStrmData is simply called here
       //ReadStrmData();
       //otherwise ReadStrmData is commented out as it is here and instead
it sends a message for the thread code.
       SendMessage(.....);
    }

This function is the function used when the music thread is created. Of
course it's not used at all when there is not a separate thread for music.

static void StrmThread(void * /*arg */ )
{
    //----

    while (1)
    {
        ReceiveMessage(...);
        (void)ReadStrmData();
    }
}


-Remember when a thread is not being used for the music no crashes occur at
all. Whenever music is using it's own thread crashes will either occur
inside ov_read called from ReadStrmData or at random points in the main
thread where it wouldn't crash otherwise if there is not a separate thread.
I've have read some information I found in your documentation about using
ov_open_callbacks when it comes to threads. I was not sure how to adjust
that so that could potentially be related to the problem. If anyone has any
suggestions on what I could change and if maybe something stands out about
it not being thread safe let me know. I could also show more code or
perhaps go into more detail at certain points if that helps.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.xiph.org/pipermail/vorbis-dev/attachments/20120526/8c6becbf/attachment.htm 


More information about the Vorbis-dev mailing list