ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

theora_decoder.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 /*
00024  * Source is based on the player example from libvorbis package,
00025  * available at: http://svn.xiph.org/trunk/theora/examples/player_example.c
00026  *
00027  * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.
00028  * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
00029  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
00030  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
00031  *
00032  * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009
00033  * by the Xiph.Org Foundation and contributors http://www.xiph.org/
00034  *
00035  */
00036 
00037 #include "video/theora_decoder.h"
00038 
00039 #include "audio/audiostream.h"
00040 #include "audio/decoders/raw.h"
00041 #include "common/stream.h"
00042 #include "common/system.h"
00043 #include "common/textconsole.h"
00044 #include "common/util.h"
00045 #include "graphics/pixelformat.h"
00046 #include "graphics/yuv_to_rgb.h"
00047 
00048 namespace Video {
00049 
00050 TheoraDecoder::TheoraDecoder() {
00051     _fileStream = 0;
00052 
00053     _videoTrack = 0;
00054     _audioTrack = 0;
00055     _hasVideo = _hasAudio = false;
00056 }
00057 
00058 TheoraDecoder::~TheoraDecoder() {
00059     close();
00060 }
00061 
00062 bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) {
00063     close();
00064 
00065     _fileStream = stream;
00066 
00067     // start up Ogg stream synchronization layer
00068     ogg_sync_init(&_oggSync);
00069 
00070     // init supporting Vorbis structures needed in header parsing
00071     vorbis_info_init(&_vorbisInfo);
00072     vorbis_comment vorbisComment;
00073     vorbis_comment_init(&vorbisComment);
00074 
00075     // init supporting Theora structures needed in header parsing
00076     th_info theoraInfo;
00077     th_info_init(&theoraInfo);
00078     th_comment theoraComment;
00079     th_comment_init(&theoraComment);
00080     th_setup_info *theoraSetup = 0;
00081 
00082     uint theoraPackets = 0, vorbisPackets = 0;
00083 
00084     // Ogg file open; parse the headers
00085     // Only interested in Vorbis/Theora streams
00086     bool foundHeader = false;
00087     while (!foundHeader) {
00088         int ret = bufferData();
00089 
00090         if (ret == 0)
00091             break; // FIXME: Shouldn't this error out?
00092 
00093         while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
00094             ogg_stream_state test;
00095 
00096             // is this a mandated initial header? If not, stop parsing
00097             if (!ogg_page_bos(&_oggPage)) {
00098                 // don't leak the page; get it into the appropriate stream
00099                 queuePage(&_oggPage);
00100                 foundHeader = true;
00101                 break;
00102             }
00103 
00104             ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
00105             ogg_stream_pagein(&test, &_oggPage);
00106             ogg_stream_packetout(&test, &_oggPacket);
00107 
00108             // identify the codec: try theora
00109             if (theoraPackets == 0 && th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket) >= 0) {
00110                 // it is theora
00111                 memcpy(&_theoraOut, &test, sizeof(test));
00112                 theoraPackets = 1;
00113                 _hasVideo = true;
00114             } else if (vorbisPackets == 0 && vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket) >= 0) {
00115                 // it is vorbis
00116                 memcpy(&_vorbisOut, &test, sizeof(test));
00117                 vorbisPackets = 1;
00118                 _hasAudio = true;
00119             } else {
00120                 // whatever it is, we don't care about it
00121                 ogg_stream_clear(&test);
00122             }
00123         }
00124         // fall through to non-bos page parsing
00125     }
00126 
00127     // we're expecting more header packets.
00128     while ((theoraPackets && theoraPackets < 3) || (vorbisPackets && vorbisPackets < 3)) {
00129         int ret;
00130 
00131         // look for further theora headers
00132         while (theoraPackets && (theoraPackets < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
00133             if (ret < 0)
00134                 error("Error parsing Theora stream headers; corrupt stream?");
00135 
00136             if (!th_decode_headerin(&theoraInfo, &theoraComment, &theoraSetup, &_oggPacket))
00137                 error("Error parsing Theora stream headers; corrupt stream?");
00138 
00139             theoraPackets++;
00140         }
00141 
00142         // look for more vorbis header packets
00143         while (vorbisPackets && (vorbisPackets < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
00144             if (ret < 0)
00145                 error("Error parsing Vorbis stream headers; corrupt stream?");
00146 
00147             if (vorbis_synthesis_headerin(&_vorbisInfo, &vorbisComment, &_oggPacket))
00148                 error("Error parsing Vorbis stream headers; corrupt stream?");
00149 
00150             vorbisPackets++;
00151 
00152             if (vorbisPackets == 3)
00153                 break;
00154         }
00155 
00156         // The header pages/packets will arrive before anything else we
00157         // care about, or the stream is not obeying spec
00158 
00159         if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
00160             queuePage(&_oggPage); // demux into the appropriate stream
00161         } else {
00162             ret = bufferData(); // someone needs more data
00163 
00164             if (ret == 0)
00165                 error("End of file while searching for codec headers.");
00166         }
00167     }
00168 
00169     // And now we have it all. Initialize decoders next
00170     if (_hasVideo) {
00171         _videoTrack = new TheoraVideoTrack(getDefaultHighColorFormat(), theoraInfo, theoraSetup);
00172         addTrack(_videoTrack);
00173     }
00174 
00175     th_info_clear(&theoraInfo);
00176     th_comment_clear(&theoraComment);
00177     th_setup_free(theoraSetup);
00178 
00179     if (_hasAudio) {
00180         _audioTrack = new VorbisAudioTrack(getSoundType(), _vorbisInfo);
00181 
00182         // Get enough audio data to start us off
00183         while (!_audioTrack->hasAudio()) {
00184             // Queue more data
00185             bufferData();
00186             while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
00187                 queuePage(&_oggPage);
00188 
00189             queueAudio();
00190         }
00191 
00192         addTrack(_audioTrack);
00193     }
00194 
00195     vorbis_comment_clear(&vorbisComment);
00196 
00197     return true;
00198 }
00199 
00200 void TheoraDecoder::close() {
00201     VideoDecoder::close();
00202 
00203     if (!_fileStream)
00204         return;
00205 
00206     if (_videoTrack) {
00207         ogg_stream_clear(&_theoraOut);
00208         _videoTrack = 0;
00209     }
00210 
00211     if (_audioTrack) {
00212         ogg_stream_clear(&_vorbisOut);
00213         _audioTrack = 0;
00214     }
00215 
00216     ogg_sync_clear(&_oggSync);
00217     vorbis_info_clear(&_vorbisInfo);
00218 
00219     delete _fileStream;
00220     _fileStream = 0;
00221 
00222     _hasVideo = _hasAudio = false;
00223 }
00224 
00225 void TheoraDecoder::readNextPacket() {
00226     // First, let's get our frame
00227     if (_hasVideo) {
00228         while (!_videoTrack->endOfTrack()) {
00229             // theora is one in, one out...
00230             if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {
00231                 if (_videoTrack->decodePacket(_oggPacket))
00232                     break;
00233             } else if (_theoraOut.e_o_s || _fileStream->eos()) {
00234                 // If we can't get any more frames, we're done.
00235                 _videoTrack->setEndOfVideo();
00236             } else {
00237                 // Queue more data
00238                 bufferData();
00239                 while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
00240                     queuePage(&_oggPage);
00241             }
00242 
00243             // Update audio if we can
00244             queueAudio();
00245         }
00246     }
00247 
00248     // Then make sure we have enough audio buffered
00249     ensureAudioBufferSize();
00250 }
00251 
00252 TheoraDecoder::TheoraVideoTrack::TheoraVideoTrack(const Graphics::PixelFormat &format, th_info &theoraInfo, th_setup_info *theoraSetup) {
00253     _theoraDecode = th_decode_alloc(&theoraInfo, theoraSetup);
00254 
00255     if (theoraInfo.pixel_fmt != TH_PF_420)
00256         error("Only theora YUV420 is supported");
00257 
00258     int postProcessingMax;
00259     th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &postProcessingMax, sizeof(postProcessingMax));
00260     th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &postProcessingMax, sizeof(postProcessingMax));
00261 
00262     _surface.create(theoraInfo.frame_width, theoraInfo.frame_height, format);
00263 
00264     // Set up a display surface
00265     _displaySurface.init(theoraInfo.pic_width, theoraInfo.pic_height, _surface.pitch,
00266                         _surface.getBasePtr(theoraInfo.pic_x, theoraInfo.pic_y), format);
00267 
00268     // Set the frame rate
00269     _frameRate = Common::Rational(theoraInfo.fps_numerator, theoraInfo.fps_denominator);
00270 
00271     _endOfVideo = false;
00272     _nextFrameStartTime = 0.0;
00273     _curFrame = -1;
00274 }
00275 
00276 TheoraDecoder::TheoraVideoTrack::~TheoraVideoTrack() {
00277     th_decode_free(_theoraDecode);
00278 
00279     _surface.free();
00280     _displaySurface.setPixels(0);
00281 }
00282 
00283 bool TheoraDecoder::TheoraVideoTrack::decodePacket(ogg_packet &oggPacket) {
00284     if (th_decode_packetin(_theoraDecode, &oggPacket, 0) == 0) {
00285         _curFrame++;
00286 
00287         // Convert YUV data to RGB data
00288         th_ycbcr_buffer yuv;
00289         th_decode_ycbcr_out(_theoraDecode, yuv);
00290         translateYUVtoRGBA(yuv);
00291 
00292         double time = th_granule_time(_theoraDecode, oggPacket.granulepos);
00293 
00294         // We need to calculate when the next frame should be shown
00295         // This is all in floating point because that's what the Ogg code gives us
00296         // Ogg is a lossy container format, so it doesn't always list the time to the
00297         // next frame. In such cases, we need to calculate it ourselves.
00298         if (time == -1.0)
00299             _nextFrameStartTime += _frameRate.getInverse().toDouble();
00300         else
00301             _nextFrameStartTime = time;
00302 
00303         return true;
00304     }
00305 
00306     return false;
00307 }
00308 
00309 enum TheoraYUVBuffers {
00310     kBufferY = 0,
00311     kBufferU = 1,
00312     kBufferV = 2
00313 };
00314 
00315 void TheoraDecoder::TheoraVideoTrack::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) {
00316     // Width and height of all buffers have to be divisible by 2.
00317     assert((YUVBuffer[kBufferY].width & 1) == 0);
00318     assert((YUVBuffer[kBufferY].height & 1) == 0);
00319     assert((YUVBuffer[kBufferU].width & 1) == 0);
00320     assert((YUVBuffer[kBufferV].width & 1) == 0);
00321 
00322     // UV images have to have a quarter of the Y image resolution
00323     assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1);
00324     assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1);
00325     assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1);
00326     assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1);
00327 
00328     YUVToRGBMan.convert420(&_surface, Graphics::YUVToRGBManager::kScaleITU, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride);
00329 }
00330 
00331 static vorbis_info *info = 0;
00332 
00333 TheoraDecoder::VorbisAudioTrack::VorbisAudioTrack(Audio::Mixer::SoundType soundType, vorbis_info &vorbisInfo) :
00334         AudioTrack(soundType) {
00335     vorbis_synthesis_init(&_vorbisDSP, &vorbisInfo);
00336     vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
00337     info = &vorbisInfo;
00338 
00339     _audStream = Audio::makeQueuingAudioStream(vorbisInfo.rate, vorbisInfo.channels != 1);
00340 
00341     _audioBufferFill = 0;
00342     _audioBuffer = 0;
00343     _endOfAudio = false;
00344 }
00345 
00346 TheoraDecoder::VorbisAudioTrack::~VorbisAudioTrack() {
00347     vorbis_dsp_clear(&_vorbisDSP);
00348     vorbis_block_clear(&_vorbisBlock);
00349     delete _audStream;
00350     free(_audioBuffer);
00351 }
00352 
00353 Audio::AudioStream *TheoraDecoder::VorbisAudioTrack::getAudioStream() const {
00354     return _audStream;
00355 }
00356 
00357 #define AUDIOFD_FRAGSIZE 10240
00358 
00359 #ifndef USE_TREMOR
00360 static double rint(double v) {
00361     return floor(v + 0.5);
00362 }
00363 #endif
00364 
00365 bool TheoraDecoder::VorbisAudioTrack::decodeSamples() {
00366 #ifdef USE_TREMOR
00367     ogg_int32_t **pcm;
00368 #else
00369     float **pcm;
00370 #endif
00371 
00372     // if there's pending, decoded audio, grab it
00373     int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm);
00374 
00375     if (ret > 0) {
00376         if (!_audioBuffer) {
00377             _audioBuffer = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t));
00378             assert(_audioBuffer);
00379         }
00380 
00381         int channels = _audStream->isStereo() ? 2 : 1;
00382         int count = _audioBufferFill / 2;
00383         int maxsamples = ((AUDIOFD_FRAGSIZE - _audioBufferFill) / channels) >> 1;
00384         int i;
00385 
00386         for (i = 0; i < ret && i < maxsamples; i++) {
00387             for (int j = 0; j < channels; j++) {
00388 #ifdef USE_TREMOR
00389                 int val = CLIP((int)pcm[j][i] >> 9, -32768, 32767);
00390 #else
00391                 int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767);
00392 #endif
00393                 _audioBuffer[count++] = val;
00394             }
00395         }
00396 
00397         vorbis_synthesis_read(&_vorbisDSP, i);
00398         _audioBufferFill += (i * channels) << 1;
00399 
00400         if (_audioBufferFill == AUDIOFD_FRAGSIZE) {
00401             byte flags = Audio::FLAG_16BITS;
00402 
00403             if (_audStream->isStereo())
00404                 flags |= Audio::FLAG_STEREO;
00405 
00406 #ifdef SCUMM_LITTLE_ENDIAN
00407             flags |= Audio::FLAG_LITTLE_ENDIAN;
00408 #endif
00409 
00410             _audStream->queueBuffer((byte *)_audioBuffer, AUDIOFD_FRAGSIZE, DisposeAfterUse::YES, flags);
00411 
00412             // The audio mixer is now responsible for the old audio buffer.
00413             // We need to create a new one.
00414             _audioBuffer = 0;
00415             _audioBufferFill = 0;
00416         }
00417 
00418         return true;
00419     }
00420 
00421     return false;
00422 }
00423 
00424 bool TheoraDecoder::VorbisAudioTrack::hasAudio() const {
00425     return _audStream->numQueuedStreams() > 0;
00426 }
00427 
00428 bool TheoraDecoder::VorbisAudioTrack::needsAudio() const {
00429     // TODO: 5 is very arbitrary. We probably should do something like QuickTime does.
00430     return !_endOfAudio && _audStream->numQueuedStreams() < 5;
00431 }
00432 
00433 void TheoraDecoder::VorbisAudioTrack::synthesizePacket(ogg_packet &oggPacket) {
00434     if (vorbis_synthesis(&_vorbisBlock, &oggPacket) == 0) // test for success
00435         vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock);
00436 }
00437 
00438 void TheoraDecoder::queuePage(ogg_page *page) {
00439     if (_hasVideo)
00440         ogg_stream_pagein(&_theoraOut, page);
00441 
00442     if (_hasAudio)
00443         ogg_stream_pagein(&_vorbisOut, page);
00444 }
00445 
00446 int TheoraDecoder::bufferData() {
00447     char *buffer = ogg_sync_buffer(&_oggSync, 4096);
00448     int bytes = _fileStream->read(buffer, 4096);
00449 
00450     ogg_sync_wrote(&_oggSync, bytes);
00451 
00452     return bytes;
00453 }
00454 
00455 bool TheoraDecoder::queueAudio() {
00456     if (!_hasAudio)
00457         return false;
00458 
00459     bool queuedAudio = false;
00460 
00461     for (;;) {
00462         if (_audioTrack->decodeSamples()) {
00463             // we queued some pending audio
00464             queuedAudio = true;
00465         } else if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) {
00466             // no pending audio; is there a pending packet to decode?
00467             _audioTrack->synthesizePacket(_oggPacket);
00468         } else {
00469             // we've buffered all we have, break out for now
00470             break;
00471         }
00472     }
00473 
00474     return queuedAudio;
00475 }
00476 
00477 void TheoraDecoder::ensureAudioBufferSize() {
00478     if (!_hasAudio)
00479         return;
00480 
00481     // Force at least some audio to be buffered
00482     while (_audioTrack->needsAudio()) {
00483         bufferData();
00484         while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0)
00485             queuePage(&_oggPage);
00486 
00487         bool queuedAudio = queueAudio();
00488         if ((_vorbisOut.e_o_s  || _fileStream->eos()) && !queuedAudio) {
00489             _audioTrack->setEndOfAudio();
00490             break;
00491         }
00492     }
00493 }
00494 
00495 } // End of namespace Video


Generated on Sat May 25 2019 05:00:55 for ResidualVM by doxygen 1.7.1
curved edge   curved edge