[theora] Newbie: How to rewind a videostream (long)

Rene Jensen centipede at takhis.net
Sun Aug 6 05:01:24 PDT 2006


Hello, I'm new to this list, and pretty much also to the codec universe.

In a computer game I'm working on, there is an 3D TV-screen onto which I 
project the frames of a movie. The movie is of course stored as theora, no 
audio as it's not needed yet. I have it working so far, extracting a frame 
and uploading it to the graphics card when the scene is drawn. The example 
file dump_video.c gave me most of what I needed.

...But I can't seem to handle end-of-stream very well. I would like to be 
able to rewind. First I tried the brute force method:

if (videoStream.eof())
{
     ogg_stream_clear (&theoraStreamState);
     theora_clear (&theoraState);
     theora_comment_clear (&theoraComment);
     theora_info_clear (&theoraInfo);
     ogg_sync_clear (&oggSyncState);
     videoStream.clear();
     videoStream.close();

     init_the_movie_like_it_was_inited_the_first_time();
}

But I got a crash when trying to re-initialize:
     theora_decode_init (&theoraState, &theoraInfo);

Specifically this line in quant.c gave me a problem:
     qmat = ci->range_table[0]->qmat;

According to my debugger (Visual Studio 7.1) ci->range_table[0] = 
0x00000000.
No doubt the problem is related to some sort of clearing that I still need 
to do.

Alternatively, I want to seek to the proper position in the file and.... 
eh... just continue reading? Truth is I don't really know what I'm doing 
here....

Here's the complete decoder-file (VideoStreamTheora.h and 
VideoStreamTheora.cpp) for reference. If you answer, perhaps you should cut 
it away from the reply :-)


Many thanks for the free codecs.




------------- HEADER FILE -------------

#ifndef VIDEOSTREAMTHEORA_H_
#define VIDEOSTREAMTHEORA_H_

#include <fstream>
#include <ogg/ogg.h>
#include <theora/theora.h>
#include "VideoStreamable.h"

namespace camilla
{
    class VideoStreamTheora : public VideoStreamable
    {

        int                    movieLengthBytes;
        int                    moviePositionBytes;

        ogg_sync_state         oggSyncState;
        ogg_page               oggPage;
        ogg_packet             oggPacket;
        ogg_stream_state       vorbisStreamState;
        ogg_stream_state       theoraStreamState;
        theora_info            theoraInfo;
        theora_comment         theoraComment;
        theora_state           theoraState;
        int                    theoraPageCount;
        yuv_buffer             yuvBuffer;

        std::ifstream          videoStream;

        void                   rewind ();
        void                   initMovie ();
        void                   exitMovie ();
        int                    buffer_data ();
        int                    queue_page (ogg_page *oggPage);
        void                   decode_frame ();

    public:

        VideoStreamTheora();
        virtual ~VideoStreamTheora();
        virtual void           decodeAndRenderFrameToTexture 
(irr::video::ITexture* texture);
    };
}

#endif /*VIDEOSTREAMTHEORA_H_*/


-------------SOURCE FILE -------------


#include <iostream>
#include "VideoStreamTheora.h"

namespace camilla
{
    VideoStreamable::~VideoStreamable()
    {
    }


    VideoStreamTheora::VideoStreamTheora()
    {
        theoraPageCount = 0;
        initMovie();
    }

    void VideoStreamTheora::rewind ()
    {
        videoStream.clear();
        videoStream.close();
        videoStream.open ("test.ogg", std::ios::binary);
        videoStream.seekg (0, std::ios::beg);
    }

    void VideoStreamTheora::exitMovie ()
    {
        ogg_stream_clear (&theoraStreamState);
        theora_clear (&theoraState);
        theora_comment_clear (&theoraComment);
        theora_info_clear (&theoraInfo);
        ogg_sync_clear (&oggSyncState);

        videoStream.clear();
        videoStream.close();
    }

    void VideoStreamTheora::initMovie ()
    {
        videoStream.open ("shit.ogg", std::ios::binary);
        if (! videoStream.good())
            throw "Error: VideoStreamTheora::initMovie: bad file.";


        videoStream.seekg (0, std::ios::end);
        movieLengthBytes = videoStream.tellg();
        videoStream.seekg (0, std::ios::beg);

        ogg_sync_init (&oggSyncState);
        theora_comment_init (&theoraComment);
        theora_info_init (&theoraInfo);

        // Fetch from the ogg-stream until we have the INITIAL PAGES of the 
theora stream
        for (bool doneScanningHeaders=false; ! doneScanningHeaders; )
        {
            if (buffer_data ()==0)
                throw "Error: VideoStreamTheora::initMovie: Couldn't get 
initial packets.";

            while (ogg_sync_pageout (&oggSyncState, &oggPage)>0)
            {
                ogg_stream_state testStreamState;

                if (! ogg_page_bos (&oggPage))
                {
                    queue_page (&oggPage);
                    doneScanningHeaders = true;
                    break;
                }

                ogg_stream_init (&testStreamState, ogg_page_serialno 
(&oggPage));
                ogg_stream_pagein (&testStreamState, &oggPage);
                ogg_stream_packetout (&testStreamState, &oggPacket);

                if(theoraPageCount==0 && theora_decode_header 
(&theoraInfo,&theoraComment, &oggPacket) >= 0)
                {
                    memcpy (&theoraStreamState, 
&testStreamState,sizeof(testStreamState));
                    theoraPageCount = 1;
                }
                else
                {
                    ogg_stream_clear (&testStreamState);
                }
            }
        }

        while (theoraPageCount && (theoraPageCount < 3))
        {
            int ret;

            while (theoraPageCount && (theoraPageCount < 3) && 
(ret=ogg_stream_packetout (&theoraStreamState, &oggPacket)))
            {
                if (ret<0)
                    throw "Error parsing Theora stream headers; corrupt 
stream?";
                if (theora_decode_header (&theoraInfo, &theoraComment, 
&oggPacket))
                    throw "Error parsing Theora stream headers; corrupt 
stream?";

                ++ theoraPageCount;

                if (theoraPageCount==3)
                    break;
            }

            if (ogg_sync_pageout (&oggSyncState, &oggPage) > 0)
                queue_page (&oggPage);
            else
            {
                if (buffer_data () == 0)
                    throw "End of file while searching for codec headers.";
            }
        }

        if (theoraPageCount > 0)
        {
            theora_decode_init (&theoraState, &theoraInfo);
            std::cout << "Ogg logical stream " << theoraStreamState.serialno
                      << " is Theora " << theoraInfo.width << "x" << 
theoraInfo.height
                      << " " << 
((double)theoraInfo.fps_numerator/theoraInfo.fps_denominator) << " fps video 
"
                      << " Encoded frame content is size: " 
<<theoraInfo.frame_width <<  "x" << theoraInfo.frame_height
                      << " offset: " <<  theoraInfo.offset_x << "," << 
theoraInfo.offset_y
                      << " Colorspace: " << theoraInfo.colorspace
                      << std::endl;
        }
        else
        {
            theora_info_clear (&theoraInfo);
            theora_comment_clear (&theoraComment);
            throw "Bad theora file. Quitting now.";
        }

        while (ogg_sync_pageout(&oggSyncState, &oggPage)>0)
        {
            queue_page (&oggPage);
        }

        decode_frame();
    }


    VideoStreamTheora::~VideoStreamTheora()
    {
    }

    void VideoStreamTheora::decode_frame ()
    {
        ogg_int64_t  videobuf_granulepos = -1;
        double       videobuf_time = 0;

        for (bool videobuf_ready = false; ! videobuf_ready; )
        {
            if (ogg_stream_packetout (&theoraStreamState, &oggPacket) > 0)
            {
                theora_decode_packetin (&theoraState, &oggPacket);
                videobuf_granulepos = theoraState.granulepos;
                videobuf_time = theora_granule_time (&theoraState, 
videobuf_granulepos);
                videobuf_ready = true;
            }

            if (! videobuf_ready && videoStream.eof())
            {
                //throw "Can't decode the frame! EOF reached";
                return;
            }

            if (! videobuf_ready)
            {
                    buffer_data ();
                    while (ogg_sync_pageout (&oggSyncState, &oggPage) > 0) {
                        queue_page (&oggPage);
                    }
            }
        }
        theora_decode_YUVout (&theoraState, &yuvBuffer);
    }




    int VideoStreamTheora::buffer_data ()
    {
        char *buffer = ogg_sync_buffer (&oggSyncState,4096);
        int bytes = videoStream.tellg();
        videoStream.read (buffer, 4096);
        bytes = int(videoStream.tellg()) - bytes;
        ogg_sync_wrote (&oggSyncState, bytes);
        return(bytes);
    }

    int VideoStreamTheora::queue_page (ogg_page *oggPage)
    {
        if(theoraPageCount > 0) ogg_stream_pagein (&theoraStreamState, 
oggPage);
        return 0;
    }

    void VideoStreamTheora::decodeAndRenderFrameToTexture 
(irr::video::ITexture* texture)
    {
        using namespace irr;
        using namespace irr::core;

        moviePositionBytes = videoStream.tellg();

        // DIDN'T WORK:
        //if (ogg_page_eos (&oggPage))

        // DIDN'T WORK:
        //if (moviePositionBytes == movieLengthBytes)

        // EOF BEING DETECTED, BUT THE REWIND / EXIT+INIT DOESN'T:
        if (videoStream.eof())
        {
            rewind();
            //exitMovie();
            //initMovie();
        }

        decode_frame();

        dimension2d<s32> texDim = texture->getSize();
        long* texMem = (long*) texture->lock();

        for (int y=0; y<texDim.Height; ++y)
        {
            for (int x=0; x<texDim.Width; ++x)
            {
                int Y = yuvBuffer.y [y*yuvBuffer.y_stride + x];
                int U = yuvBuffer.u [(y/2)*yuvBuffer.uv_stride + (x/2)];
                int V = yuvBuffer.v [(y/2)*yuvBuffer.uv_stride + (x/2)];
                int B = (int)(1.164*(Y - 16)                   + 2.018*(U - 
128));
                int G = (int)(1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 
128));
                int R = (int)(1.164*(Y - 16) + 1.596*(V - 128));

                *texMem++ = ((R&0xFF)<<16) | ((G&0xFF)<<8) | ((B&0xFF));
            }
        }

        texture->unlock();
    }
}




More information about the theora mailing list