[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