diff options
-rw-r--r-- | dists/msvc8/scummvm.vcproj | 11 | ||||
-rw-r--r-- | dists/msvc9/scummvm.vcproj | 11 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics.cpp | 132 | ||||
-rw-r--r-- | graphics/module.mk | 4 | ||||
-rw-r--r-- | graphics/video/avi_decoder.cpp | 399 | ||||
-rw-r--r-- | graphics/video/avi_decoder.h (renamed from graphics/video/avi_player.h) | 66 | ||||
-rw-r--r-- | graphics/video/avi_player.cpp | 383 | ||||
-rw-r--r-- | graphics/video/codecs/codec.h | 43 | ||||
-rw-r--r-- | graphics/video/codecs/msvideo1.cpp (renamed from graphics/video/msvideo1.cpp) | 2 | ||||
-rw-r--r-- | graphics/video/codecs/msvideo1.h (renamed from graphics/video/msvideo1.h) | 5 |
10 files changed, 519 insertions, 537 deletions
diff --git a/dists/msvc8/scummvm.vcproj b/dists/msvc8/scummvm.vcproj index a6a465844c..1fe48b0e9e 100644 --- a/dists/msvc8/scummvm.vcproj +++ b/dists/msvc8/scummvm.vcproj @@ -452,20 +452,23 @@ <File RelativePath="..\..\graphics\fonts\scummfont.cpp" /> </Filter> <Filter Name="video"> - <File RelativePath="..\..\graphics\video\avi_player.cpp" /> - <File RelativePath="..\..\graphics\video\avi_player.h" /> + <File RelativePath="..\..\graphics\video\avi_decoder.cpp" /> + <File RelativePath="..\..\graphics\video\avi_decoder.h" /> <File RelativePath="..\..\graphics\video\dxa_decoder.cpp" /> <File RelativePath="..\..\graphics\video\dxa_decoder.h" /> <File RelativePath="..\..\graphics\video\flic_decoder.cpp" /> <File RelativePath="..\..\graphics\video\flic_decoder.h" /> <File RelativePath="..\..\graphics\video\mpeg_player.cpp" /> <File RelativePath="..\..\graphics\video\mpeg_player.h" /> - <File RelativePath="..\..\graphics\video\msvideo1.cpp" /> - <File RelativePath="..\..\graphics\video\msvideo1.h" /> <File RelativePath="..\..\graphics\video\smk_decoder.cpp" /> <File RelativePath="..\..\graphics\video\smk_decoder.h" /> <File RelativePath="..\..\graphics\video\video_player.cpp" /> <File RelativePath="..\..\graphics\video\video_player.h" /> + <Filter Name="codecs"> + <File RelativePath="..\..\graphics\video\codecs\codec.h" /> + <File RelativePath="..\..\graphics\video\codecs\msvideo1.cpp" /> + <File RelativePath="..\..\graphics\video\codecs\msvideo1.h" /> + </Filter> <Filter Name="coktelvideo"> <File RelativePath="..\..\graphics\video\coktelvideo\coktelvideo.cpp" /> <File RelativePath="..\..\graphics\video\coktelvideo\coktelvideo.h" /> diff --git a/dists/msvc9/scummvm.vcproj b/dists/msvc9/scummvm.vcproj index 41355c20cb..e0dd6cc9a7 100644 --- a/dists/msvc9/scummvm.vcproj +++ b/dists/msvc9/scummvm.vcproj @@ -453,20 +453,23 @@ <File RelativePath="..\..\graphics\fonts\scummfont.cpp" /> </Filter> <Filter Name="video"> - <File RelativePath="..\..\graphics\video\avi_player.cpp" /> - <File RelativePath="..\..\graphics\video\avi_player.h" /> + <File RelativePath="..\..\graphics\video\avi_decoder.cpp" /> + <File RelativePath="..\..\graphics\video\avi_decoder.h" /> <File RelativePath="..\..\graphics\video\dxa_decoder.cpp" /> <File RelativePath="..\..\graphics\video\dxa_decoder.h" /> <File RelativePath="..\..\graphics\video\flic_decoder.cpp" /> <File RelativePath="..\..\graphics\video\flic_decoder.h" /> <File RelativePath="..\..\graphics\video\mpeg_player.cpp" /> <File RelativePath="..\..\graphics\video\mpeg_player.h" /> - <File RelativePath="..\..\graphics\video\msvideo1.cpp" /> - <File RelativePath="..\..\graphics\video\msvideo1.h" /> <File RelativePath="..\..\graphics\video\smk_decoder.cpp" /> <File RelativePath="..\..\graphics\video\smk_decoder.h" /> <File RelativePath="..\..\graphics\video\video_player.cpp" /> <File RelativePath="..\..\graphics\video\video_player.h" /> + <Filter Name="codecs"> + <File RelativePath="..\..\graphics\video\codecs\codec.h" /> + <File RelativePath="..\..\graphics\video\codecs\msvideo1.cpp" /> + <File RelativePath="..\..\graphics\video\codecs\msvideo1.h" /> + </Filter> <Filter Name="coktelvideo"> <File RelativePath="..\..\graphics\video\coktelvideo\coktelvideo.cpp" /> <File RelativePath="..\..\graphics\video\coktelvideo\coktelvideo.h" /> diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index c558a83b1e..1dc3422923 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -24,7 +24,7 @@ */ #include "graphics/cursorman.h" -#include "graphics/video/avi_player.h" +#include "graphics/video/avi_decoder.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -978,102 +978,6 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -static reg_t kShowMovie_Windows(EngineState *s, int argc, reg_t *argv) { - Common::String filename = s->_segMan->getString(argv[1]); - - Graphics::AVIPlayer *player = new Graphics::AVIPlayer(g_system); - - if (!player->open(filename)) { - warning("Failed to open movie file %s", filename.c_str()); - return s->r_acc; - } - - uint32 startTime = g_system->getMillis(); - bool play = true; - - while (play && player->getCurFrame() < player->getFrameCount()) { - uint32 elapsed = g_system->getMillis() - startTime; - - if (elapsed >= player->getCurFrame() * 1000 / player->getFrameRate()) { - Graphics::Surface *surface = player->getNextFrame(); - - Palette *palette = NULL; - - if (player->dirtyPalette()) { - byte *rawPalette = player->getPalette(); - Palette *colors = new Palette(256); - - byte r, g, b; - - for (uint16 i = 0; i < 256; i++) { - r = rawPalette[i * 4]; - g = rawPalette[i * 4 + 1]; - b = rawPalette[i * 4 + 2]; - colors->setColor(i, r, g, b); - } - - palette->forceInto(s->gfx_state->driver->getMode()->palette); - } - - if (surface) { - // Allocate a pixmap - gfx_pixmap_t *pixmap = gfx_new_pixmap(surface->w, surface->h, 0, 0, 0); - assert(pixmap); - gfx_pixmap_alloc_index_data(pixmap); - - // Copy data from the surface - memcpy(pixmap->index_data, surface->pixels, surface->w * surface->h); - pixmap->xoffset = (g_system->getWidth() - surface->w) / 2; - pixmap->yoffset = (g_system->getHeight() - surface->h) / 2; - pixmap->palette = palette; - - // Copy the frame to the screen - gfx_xlate_pixmap(pixmap, s->gfx_state->driver->getMode()); - gfxop_draw_pixmap(s->gfx_state, pixmap, gfx_rect(0, 0, 320, 200), Common::Point(pixmap->xoffset, pixmap->yoffset)); - gfxop_update_box(s->gfx_state, gfx_rect(0, 0, 320, 200)); - gfx_free_pixmap(pixmap); - - // Surface is freed when the codec in the video is deleted - } - } - - Common::Event event; - while (g_system->getEventManager()->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_QUIT: - play = false; - quit_vm(); - break; - default: - break; - } - } - - g_system->delayMillis(10); - } - - delete player; - - return s->r_acc; -} - -static reg_t kShowMovie_DOS(EngineState *s, int argc, reg_t *argv) { - Common::String filename = s->_segMan->getString(argv[0]); - int delay = argv[1].toUint16(); // Time between frames in ticks - - Graphics::SeqDecoder *seqDecoder = new Graphics::SeqDecoder(); - Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder); - if (seqDecoder->loadFile(filename.c_str(), delay)) - player->playVideo(); - else - warning("Failed to open movie file %s", filename.c_str()); - seqDecoder->closeFile(); - delete player; - delete seqDecoder; - - return s->r_acc; -} - reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // KQ6 Windows calls this with one argument. It doesn't seem // to have a purpose... @@ -1082,10 +986,38 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // The Windows and DOS versions use different video format as well // as a different argument set. - if (argv[0].toUint16() == 0) - return kShowMovie_Windows(s, argc, argv); + if (argv[0].toUint16() == 0) { + // Windows + + Common::String filename = s->_segMan->getString(argv[1]); + + Graphics::AviDecoder *aviDecoder = new Graphics::AviDecoder(g_system->getMixer()); + Graphics::VideoPlayer *player = new Graphics::VideoPlayer(aviDecoder); + if (aviDecoder->loadFile(filename.c_str())) + player->playVideo(); + else + warning("Failed to open movie file %s", filename.c_str()); + aviDecoder->closeFile(); + delete player; + delete aviDecoder; + } else { + // DOS - return kShowMovie_DOS(s, argc, argv); + Common::String filename = s->_segMan->getString(argv[0]); + int delay = argv[1].toUint16(); // Time between frames in ticks + + Graphics::SeqDecoder *seqDecoder = new Graphics::SeqDecoder(); + Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder); + if (seqDecoder->loadFile(filename.c_str(), delay)) + player->playVideo(); + else + warning("Failed to open movie file %s", filename.c_str()); + seqDecoder->closeFile(); + delete player; + delete seqDecoder; + } + + return s->r_acc; } reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) { diff --git a/graphics/module.mk b/graphics/module.mk index d8eece0440..31156ab1ff 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -21,13 +21,13 @@ MODULE_OBJS := \ thumbnail.o \ VectorRenderer.o \ VectorRendererSpec.o \ - video/avi_player.o \ + video/avi_decoder.o \ video/dxa_decoder.o \ video/flic_decoder.o \ video/mpeg_player.o \ - video/msvideo1.o \ video/smk_decoder.o \ video/video_player.o \ + video/codecs/msvideo1.o \ video/coktelvideo/indeo3.o \ video/coktelvideo/coktelvideo.o diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp new file mode 100644 index 0000000000..fa5494d9b9 --- /dev/null +++ b/graphics/video/avi_decoder.cpp @@ -0,0 +1,399 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/archive.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/stream.h" +#include "common/events.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +#include "graphics/video/avi_decoder.h" + +// Codecs +#include "graphics/video/codecs/msvideo1.h" + +namespace Graphics { + +AviDecoder::AviDecoder(Audio::Mixer *mixer) : _mixer(mixer) { + _videoCodec = NULL; + _decodedHeader = false; + _audStream = NULL; + _fileStream = NULL; + _audHandle = new Audio::SoundHandle(); + memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); + memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); + memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); + memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); + memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); +} + +AviDecoder::~AviDecoder() { + delete _audHandle; + closeFile(); +} + +void AviDecoder::runHandle(uint32 tag) { + assert (_fileStream); + if (_fileStream->eos()) + return; + + debug (3, "Decoding tag %s", tag2str(tag)); + + switch (tag) { + case ID_RIFF: + /*_filesize = */_fileStream->readUint32LE(); + if (_fileStream->readUint32BE() != ID_AVI) + error("RIFF file is not an AVI video"); + break; + case ID_LIST: + handleList(); + break; + case ID_AVIH: + _header.size = _fileStream->readUint32LE(); + _header.microSecondsPerFrame = _fileStream->readUint32LE(); + _header.maxBytesPerSecond = _fileStream->readUint32LE(); + _header.padding = _fileStream->readUint32LE(); + _header.flags = _fileStream->readUint32LE(); + _header.totalFrames = _fileStream->readUint32LE(); + _header.initialFrames = _fileStream->readUint32LE(); + _header.streams = _fileStream->readUint32LE(); + _header.bufferSize = _fileStream->readUint32LE(); + _header.width = _fileStream->readUint32LE(); + _header.height = _fileStream->readUint32LE(); + //Ignore 16 bytes of reserved data + _fileStream->skip(16); + break; + case ID_STRH: + handleStreamHeader(); + break; + case ID_STRD: // Extra stream info, safe to ignore + case ID_VEDT: // Unknown, safe to ignore + case ID_JUNK: // Alignment bytes, should be ignored + { + uint32 junkSize = _fileStream->readUint32LE(); + _fileStream->skip(junkSize + (junkSize & 1)); // Alignment + } break; + case ID_IDX1: + _ixInfo.size = _fileStream->readUint32LE(); + _ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16]; + debug (0, "%d Indices", (_ixInfo.size / 16)); + for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { + _ixInfo.indices[i].id = _fileStream->readUint32BE(); + _ixInfo.indices[i].flags = _fileStream->readUint32LE(); + _ixInfo.indices[i].offset = _fileStream->readUint32LE(); + _ixInfo.indices[i].size = _fileStream->readUint32LE(); + debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); + } + break; + default: + error ("Unknown tag \'%s\' found", tag2str(tag)); + } +} + +void AviDecoder::handleList() { + uint32 listSize = _fileStream->readUint32LE() - 4; // Subtract away listType's 4 bytes + uint32 listType = _fileStream->readUint32BE(); + uint32 curPos = _fileStream->pos(); + + debug (0, "Found LIST of type %s", tag2str(listType)); + + while ((_fileStream->pos() - curPos) < listSize) + runHandle(_fileStream->readUint32BE()); + + // We now have all the header data + if (listType == ID_HDRL) + _decodedHeader = true; +} + +void AviDecoder::handleStreamHeader() { + AVIStreamHeader sHeader; + sHeader.size = _fileStream->readUint32LE(); + sHeader.streamType = _fileStream->readUint32BE(); + if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) + error ("Unhandled MIDI/Text stream"); + sHeader.streamHandler = _fileStream->readUint32BE(); + sHeader.flags = _fileStream->readUint32LE(); + sHeader.priority = _fileStream->readUint16LE(); + sHeader.language = _fileStream->readUint16LE(); + sHeader.initialFrames = _fileStream->readUint32LE(); + sHeader.scale = _fileStream->readUint32LE(); + sHeader.rate = _fileStream->readUint32LE(); + sHeader.start = _fileStream->readUint32LE(); + sHeader.length = _fileStream->readUint32LE(); + sHeader.bufferSize = _fileStream->readUint32LE(); + sHeader.quality = _fileStream->readUint32LE(); + sHeader.sampleSize = _fileStream->readUint32LE(); + sHeader.frame.left = _fileStream->readSint16LE(); + sHeader.frame.top = _fileStream->readSint16LE(); + sHeader.frame.right = _fileStream->readSint16LE(); + sHeader.frame.bottom = _fileStream->readSint16LE(); + + if (_fileStream->readUint32BE() != ID_STRF) + error("Could not find STRF tag"); + /* uint32 strfSize = */ _fileStream->readUint32LE(); + + if (sHeader.streamType == ID_VIDS) { + _vidsHeader = sHeader; + + _bmInfo.size = _fileStream->readUint32LE(); + _bmInfo.width = _fileStream->readUint32LE(); + assert (_header.width == _bmInfo.width); + _bmInfo.height = _fileStream->readUint32LE(); + assert (_header.height == _bmInfo.height); + _bmInfo.planes = _fileStream->readUint16LE(); + _bmInfo.bitCount = _fileStream->readUint16LE(); + _bmInfo.compression = _fileStream->readUint32BE(); + _bmInfo.sizeImage = _fileStream->readUint32LE(); + _bmInfo.xPelsPerMeter = _fileStream->readUint32LE(); + _bmInfo.yPelsPerMeter = _fileStream->readUint32LE(); + _bmInfo.clrUsed = _fileStream->readUint32LE(); + _bmInfo.clrImportant = _fileStream->readUint32LE(); + + if (_bmInfo.bitCount == 8) { + if (_bmInfo.clrUsed == 0) + _bmInfo.clrUsed = 256; + + for (uint32 i = 0; i < _bmInfo.clrUsed; i++) { + _palette[i * 3 + 2] = _fileStream->readByte(); + _palette[i * 3 + 1] = _fileStream->readByte(); + _palette[i * 3] = _fileStream->readByte(); + /*_palette[i * 4 + 3] = */_fileStream->readByte(); + } + + setPalette(_palette); + } + } else if (sHeader.streamType == ID_AUDS) { + _audsHeader = sHeader; + + _wvInfo.tag = _fileStream->readUint16LE(); + _wvInfo.channels = _fileStream->readUint16LE(); + _wvInfo.samplesPerSec = _fileStream->readUint32LE(); + _wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); + _wvInfo.blockAlign = _fileStream->readUint16LE(); + _wvInfo.size = _fileStream->readUint16LE(); + } +} + +bool AviDecoder::loadFile(const char *fileName) { + closeFile(); + + _fileStream = SearchMan.createReadStreamForMember(fileName); + if (!_fileStream) + return false; + + _decodedHeader = false; + // Seek to the first frame + _videoInfo.currentFrame = 0; + + + // Read chunks until we have decoded the header + while (!_decodedHeader) + runHandle(_fileStream->readUint32BE()); + + _videoFrameBuffer = new byte[_header.width * _header.height]; + memset(_videoFrameBuffer, 0, _header.width * _header.height); + + uint32 nextTag = _fileStream->readUint32BE(); + + // Throw out any JUNK section + if (nextTag == ID_JUNK) { + runHandle(ID_JUNK); + nextTag = _fileStream->readUint32BE(); + } + + // Ignore the 'movi' LIST + if (nextTag == ID_LIST) { + _fileStream->readUint32BE(); // Skip size + if (_fileStream->readUint32BE() != ID_MOVI) + error ("Expected 'movi' LIST"); + } else + error ("Expected 'movi' LIST"); + + // Now, create the codec + _videoCodec = createCodec(); + + // Initialize the video stuff too + _audStream = createAudioStream(); + if (_audStream) + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream); + + debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); + debug (0, "Frame Rate = %d", getFrameRate()); + if (_header.flags & AVIF_ISINTERLEAVED) + debug (0, "Sound Rate = %d", AUDIO_RATE); + debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); + + _videoInfo.firstframeOffset = _fileStream->pos(); + _videoInfo.width = _header.width; + _videoInfo.height = _header.height; + _videoInfo.frameCount = _header.totalFrames; + // Our frameDelay is calculated in 1/100 ms, so we convert it here + _videoInfo.frameDelay = _header.microSecondsPerFrame / 10; + + return true; +} + +void AviDecoder::closeFile() { + if (!_fileStream) + return; + + delete _fileStream; + _fileStream = 0; + + delete[] _videoFrameBuffer; + _videoFrameBuffer = 0; + + // Deinitialize sound + _mixer->stopHandle(*_audHandle); + + _decodedHeader = false; + + delete _videoCodec; + delete[] _ixInfo.indices; +} + +Surface *AviDecoder::getNextFrame() { + uint32 nextTag = _fileStream->readUint32BE(); + + if (nextTag == ID_LIST) { + // A list of audio/video chunks + uint32 listSize = _fileStream->readUint32LE() - 4; + int32 startPos = _fileStream->pos(); + + if (_fileStream->readUint32BE() != ID_REC) + error ("Expected 'rec ' LIST"); + + // Decode chunks in the list and see if we get a frame + Surface *frame = NULL; + while (_fileStream->pos() < startPos + (int32)listSize) { + Surface *temp = getNextFrame(); + if (temp) + frame = temp; + } + + return frame; + } else if (getStreamType(nextTag) == 'wb') { + // Audio Chunk + uint32 chunkSize = _fileStream->readUint32LE(); + byte *data = new byte[chunkSize]; + _fileStream->read(data, chunkSize); + _audStream->queueBuffer(data, chunkSize); + _fileStream->skip(chunkSize & 1); // Alignment + } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id') { + // Compressed Frame + _videoInfo.currentFrame++; + uint32 chunkSize = _fileStream->readUint32LE(); + + if (chunkSize == 0) // Keep last frame on screen + return NULL; + + Common::SeekableReadStream *frameData = _fileStream->readStream(chunkSize); + Graphics::Surface *surface = _videoCodec->decodeImage(frameData); + delete frameData; + _fileStream->skip(chunkSize & 1); // Alignment + return surface; + } else if (getStreamType(nextTag) == 'pc') { + // Palette Change + _fileStream->readUint32LE(); // Chunk size, not needed here + byte firstEntry = _fileStream->readByte(); + uint16 numEntries = _fileStream->readByte(); + _fileStream->readUint16LE(); // Reserved + + // 0 entries means all colors are going to be changed + if (numEntries == 0) + numEntries = 256; + + for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { + _palette[i * 3] = _fileStream->readByte(); + _palette[i * 3 + 1] = _fileStream->readByte(); + _palette[i * 3 + 2] = _fileStream->readByte(); + _fileStream->readByte(); // Flags that don't serve us any purpose + } + + setPalette(_palette); + + // No alignment necessary. It's always even. + } else if (nextTag == ID_JUNK) { + runHandle(ID_JUNK); + } else + error ("Tag = \'%s\'", tag2str(nextTag)); + + return NULL; +} + +bool AviDecoder::decodeNextFrame() { + if (_videoInfo.currentFrame == 0) + _videoInfo.startTime = g_system->getMillis(); + + Surface *surface = NULL; + + while (!surface && _videoInfo.currentFrame < _videoInfo.frameCount) + surface = getNextFrame(); + + if (surface) + memcpy(_videoFrameBuffer, surface->pixels, _header.width * _header.height); + + return _videoInfo.currentFrame < _videoInfo.frameCount; +} + +Codec *AviDecoder::createCodec() { + switch (_vidsHeader.streamHandler) { + case ID_CRAM: + case ID_MSVC: + case ID_WHAM: + return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); + default: + warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); + } + + return NULL; +} + +Audio::AppendableAudioStream *AviDecoder::createAudioStream() { + if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) + return Audio::makeAppendableAudioStream(AUDIO_RATE, Audio::Mixer::FLAG_UNSIGNED|Audio::Mixer::FLAG_AUTOFREE); + + if (_wvInfo.tag != 0) // No sound + warning ("Unsupported AVI audio format %d", _wvInfo.tag); + + return NULL; +} + +byte AviDecoder::char2num(char c) { + return (c >= 48 && c <= 57) ? c - 48 : 0; +} + +byte AviDecoder::getStreamNum(uint32 tag) { + return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +} + +uint16 AviDecoder::getStreamType(uint32 tag) { + return tag & 0xffff; +} + +} // End of namespace Graphics diff --git a/graphics/video/avi_player.h b/graphics/video/avi_decoder.h index 50abe18982..85500302d0 100644 --- a/graphics/video/avi_player.h +++ b/graphics/video/avi_decoder.h @@ -26,11 +26,8 @@ #ifndef GRAPHICS_AVI_PLAYER_H #define GRAPHICS_AVI_PLAYER_H -#include "common/file.h" -#include "common/system.h" -#include "common/rect.h" -#include "common/util.h" - +#include "graphics/video/video_player.h" +#include "graphics/video/codecs/codec.h" #include "sound/audiostream.h" #include "sound/mixer.h" @@ -39,7 +36,7 @@ namespace Graphics { #define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) #define AUDIO_RATE (_audsHeader.rate / _audsHeader.scale) -// ID's That are used throughout the AVI files +// IDs used throughout the AVI files // that will be handled by this player #define ID_RIFF MKID_BE('RIFF') #define ID_AVI MKID_BE('AVI ') @@ -173,48 +170,37 @@ struct AVIStreamHeader { uint32 sampleSize; Common::Rect frame; }; - -class Codec { + +class AviDecoder : public VideoDecoder { public: - Codec() {} - virtual ~Codec() {} - virtual Graphics::Surface *decodeImage(Common::SeekableReadStream *stream) = 0; -}; + AviDecoder(Audio::Mixer *mixer); + virtual ~AviDecoder(); -class AVIPlayer { -public: - AVIPlayer(OSystem* syst); - ~AVIPlayer(); - - // Open/Load an AVI video from "filename" - bool open(Common::String filename); - - // Open/Load an AVI video from a stream - void open(Common::SeekableReadStream *stream); - - // Close the current AVI video and release any data - void close(); - - uint32 getFrameRate() { return _vidsHeader.rate / _vidsHeader.scale; } - byte *getPalette() { _dirtyPalette = false; return _palette; } - Surface *getNextFrame(); - bool dirtyPalette() { return _dirtyPalette; } - uint32 getCurFrame() { return _curFrame; } - uint32 getFrameCount() { return _header.totalFrames; } + /** + * Load an AVI video file + * @param filename the filename to load + */ + bool loadFile(const char *fileName); + + /** + * Close an AVI video file + */ + void closeFile(); + + bool decodeNextFrame(); + + int32 getFrameRate() { return _vidsHeader.rate / _vidsHeader.scale; } private: - OSystem *_system; - Common::SeekableReadStream *_stream; + Audio::Mixer *_mixer; BITMAPINFOHEADER _bmInfo; PCMWAVEFORMAT _wvInfo; AVIOLDINDEX _ixInfo; AVIHeader _header; AVIStreamHeader _vidsHeader; AVIStreamHeader _audsHeader; - byte *_palette; + byte _palette[3 * 256]; bool _decodedHeader; - uint32 _curFrame; - bool _dirtyPalette; Codec *_videoCodec; Codec *createCodec(); @@ -228,14 +214,14 @@ private: Audio::AppendableAudioStream *_audStream; Audio::AppendableAudioStream *createAudioStream(); - uint32 _filesize; - // Helper functions static byte char2num(char c); static byte getStreamNum(uint32 tag); static uint16 getStreamType(uint32 tag); + + Surface *getNextFrame(); }; -} // End of namespace JMP +} // End of namespace Graphics #endif diff --git a/graphics/video/avi_player.cpp b/graphics/video/avi_player.cpp deleted file mode 100644 index 3385aa6149..0000000000 --- a/graphics/video/avi_player.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $URL$ - * $Id$ - * - */ - -#include "common/endian.h" -#include "common/file.h" -#include "common/stream.h" -#include "common/events.h" - -#include "sound/audiostream.h" -#include "sound/mixer.h" - -#include "graphics/video/avi_player.h" - -// Codecs -#include "graphics/video/msvideo1.h" - -namespace Graphics { - -AVIPlayer::AVIPlayer(OSystem* syst) : _system(syst) { - _videoCodec = NULL; - _palette = NULL; - _decodedHeader = false; - _filesize = 0; - _curFrame = 0; - _audStream = NULL; - _dirtyPalette = false; - _stream = NULL; - _audHandle = new Audio::SoundHandle(); - memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); - memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); - memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); - memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); -} - -AVIPlayer::~AVIPlayer() { - close(); - delete _audHandle; -} - -void AVIPlayer::runHandle(uint32 tag) { - assert (_stream); - if (_stream->eos()) - return; - - debug (3, "Decoding tag %s", tag2str(tag)); - - switch (tag) { - case ID_RIFF: - _filesize = _stream->readUint32LE(); - assert(_stream->readUint32BE() == ID_AVI); - break; - case ID_LIST: - handleList(); - break; - case ID_AVIH: - _header.size = _stream->readUint32LE(); - _header.microSecondsPerFrame = _stream->readUint32LE(); - _header.maxBytesPerSecond = _stream->readUint32LE(); - _header.padding = _stream->readUint32LE(); - _header.flags = _stream->readUint32LE(); - _header.totalFrames = _stream->readUint32LE(); - _header.initialFrames = _stream->readUint32LE(); - _header.streams = _stream->readUint32LE(); - _header.bufferSize = _stream->readUint32LE(); - _header.width = _stream->readUint32LE(); - _header.height = _stream->readUint32LE(); - //Ignore 16 bytes of reserved data - _stream->skip(16); - break; - case ID_STRH: - handleStreamHeader(); - break; - case ID_STRD: // Extra stream info, safe to ignore - case ID_VEDT: // Unknown, safe to ignore - case ID_JUNK: // Alignment bytes, should be ignored - { - uint32 junkSize = _stream->readUint32LE(); - _stream->skip(junkSize + (junkSize & 1)); // Alignment - } break; - case ID_IDX1: - _ixInfo.size = _stream->readUint32LE(); - _ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16]; - debug (0, "%d Indices", (_ixInfo.size / 16)); - for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { - _ixInfo.indices[i].id = _stream->readUint32BE(); - _ixInfo.indices[i].flags = _stream->readUint32LE(); - _ixInfo.indices[i].offset = _stream->readUint32LE(); - _ixInfo.indices[i].size = _stream->readUint32LE(); - debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); - } - break; - default: error ("Unknown tag \'%s\' found", tag2str(tag)); - } -} - -void AVIPlayer::handleList() { - uint32 listSize = _stream->readUint32LE() - 4; // Subtract away listType's 4 bytes - uint32 listType = _stream->readUint32BE(); - uint32 curPos = _stream->pos(); - - debug (0, "Found LIST of type %s", tag2str(listType)); - - while ((_stream->pos() - curPos) < listSize) - runHandle(_stream->readUint32BE()); - - // We now have all the header data - if (listType == ID_HDRL) - _decodedHeader = true; -} - -void AVIPlayer::handleStreamHeader() { - AVIStreamHeader sHeader; - sHeader.size = _stream->readUint32LE(); - sHeader.streamType = _stream->readUint32BE(); - if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) - error ("Unhandled MIDI/Text stream"); - sHeader.streamHandler = _stream->readUint32BE(); - sHeader.flags = _stream->readUint32LE(); - sHeader.priority = _stream->readUint16LE(); - sHeader.language = _stream->readUint16LE(); - sHeader.initialFrames = _stream->readUint32LE(); - sHeader.scale = _stream->readUint32LE(); - sHeader.rate = _stream->readUint32LE(); - sHeader.start = _stream->readUint32LE(); - sHeader.length = _stream->readUint32LE(); - sHeader.bufferSize = _stream->readUint32LE(); - sHeader.quality = _stream->readUint32LE(); - sHeader.sampleSize = _stream->readUint32LE(); - sHeader.frame.left = _stream->readSint16LE(); - sHeader.frame.top = _stream->readSint16LE(); - sHeader.frame.right = _stream->readSint16LE(); - sHeader.frame.bottom = _stream->readSint16LE(); - - assert (_stream->readUint32BE() == ID_STRF); - /* uint32 strfSize = */ _stream->readUint32LE(); - - if (sHeader.streamType == ID_VIDS) { - _vidsHeader = sHeader; - - _bmInfo.size = _stream->readUint32LE(); - _bmInfo.width = _stream->readUint32LE(); - assert (_header.width == _bmInfo.width); - _bmInfo.height = _stream->readUint32LE(); - assert (_header.height == _bmInfo.height); - _bmInfo.planes = _stream->readUint16LE(); - _bmInfo.bitCount = _stream->readUint16LE(); - _bmInfo.compression = _stream->readUint32BE(); - _bmInfo.sizeImage = _stream->readUint32LE(); - _bmInfo.xPelsPerMeter = _stream->readUint32LE(); - _bmInfo.yPelsPerMeter = _stream->readUint32LE(); - _bmInfo.clrUsed = _stream->readUint32LE(); - _bmInfo.clrImportant = _stream->readUint32LE(); - - if (_bmInfo.bitCount == 8) { - if (_bmInfo.clrUsed == 0) - _bmInfo.clrUsed = 256; - - _palette = (byte *)malloc(256 * 4); - - for (uint32 i = 0; i < _bmInfo.clrUsed; i++) { - _palette[i * 4 + 2] = _stream->readByte(); - _palette[i * 4 + 1] = _stream->readByte(); - _palette[i * 4] = _stream->readByte(); - _palette[i * 4 + 3] = _stream->readByte(); - } - - // Assign the palette to be dirty - _dirtyPalette = true; - } - } else if (sHeader.streamType == ID_AUDS) { - _audsHeader = sHeader; - - _wvInfo.tag = _stream->readUint16LE(); - _wvInfo.channels = _stream->readUint16LE(); - _wvInfo.samplesPerSec = _stream->readUint32LE(); - _wvInfo.avgBytesPerSec = _stream->readUint32LE(); - _wvInfo.blockAlign = _stream->readUint16LE(); - _wvInfo.size = _stream->readUint16LE(); - } -} - -bool AVIPlayer::open(Common::String filename) { - Common::File *file = new Common::File(); - - if (!file->open(filename.c_str())) - return false; - - open(file); - - return true; -} - -void AVIPlayer::open(Common::SeekableReadStream *stream) { - close(); - - assert(stream); - _stream = stream; - - _decodedHeader = false; - _curFrame = 0; - - // Read chunks until we have decoded the header - while (!_decodedHeader) - runHandle(_stream->readUint32BE()); - - uint32 nextTag = _stream->readUint32BE(); - - // Throw out any JUNK section - if (nextTag == ID_JUNK) { - runHandle(ID_JUNK); - nextTag = _stream->readUint32BE(); - } - - // Ignore the 'movi' LIST - if (nextTag == ID_LIST) { - _stream->readUint32BE(); // Skip size - if (_stream->readUint32BE() != ID_MOVI) - error ("Expected 'movi' LIST"); - } else - error ("Expected 'movi' LIST"); - - // Now, create the codec - _videoCodec = createCodec(); - - // Initialize the video stuff too - _audStream = createAudioStream(); - if (_audStream) - _system->getMixer()->playInputStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream); - - debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); - debug (0, "Frame Rate = %d", getFrameRate()); - if (_header.flags & AVIF_ISINTERLEAVED) - debug (0, "Sound Rate = %d", AUDIO_RATE); - debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); -} - -void AVIPlayer::close() { - delete _stream; - - // Deinitialize sound - _system->getMixer()->stopHandle(*_audHandle); - - if (_palette) { - free(_palette); - _palette = NULL; - } - - _decodedHeader = false; - _filesize = 0; - - delete _videoCodec; - delete[] _ixInfo.indices; -} - -Surface *AVIPlayer::getNextFrame() { - uint32 nextTag = _stream->readUint32BE(); - - if (nextTag == ID_LIST) { - // A list of audio/video chunks - uint32 listSize = _stream->readUint32LE() - 4; - int32 startPos = _stream->pos(); - - if (_stream->readUint32BE() != ID_REC) - error ("Expected 'rec ' LIST"); - - // Decode chunks in the list and see if we get a frame - Surface *frame = NULL; - while (_stream->pos() < startPos + (int32)listSize) { - Surface *temp = getNextFrame(); - if (temp) - frame = temp; - } - - return frame; - } else if (getStreamType(nextTag) == 'wb') { - // Audio Chunk - uint32 chunkSize = _stream->readUint32LE(); - byte *data = new byte[chunkSize]; - _stream->read(data, chunkSize); - _audStream->queueBuffer(data, chunkSize); - _stream->skip(chunkSize & 1); // Alignment - } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id') { - // Compressed Frame - _curFrame++; - uint32 chunkSize = _stream->readUint32LE(); - - if (chunkSize == 0) // Keep last frame on screen - return NULL; - - Common::SeekableReadStream *frameData = _stream->readStream(chunkSize); - Graphics::Surface *surface = _videoCodec->decodeImage(frameData); - delete frameData; - _stream->skip(chunkSize & 1); // Alignment - return surface; - } else if (getStreamType(nextTag) == 'pc') { - // Palette Change - _stream->readUint32LE(); // Chunk size, not needed here - byte firstEntry = _stream->readByte(); - uint16 numEntries = _stream->readByte(); - _stream->readUint16LE(); // Reserved - - // 0 entries means all colors are going to be changed - if (numEntries == 0) - numEntries = 256; - - for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { - _palette[i * 4] = _stream->readByte(); - _palette[i * 4 + 1] = _stream->readByte(); - _palette[i * 4 + 2] = _stream->readByte(); - _stream->readByte(); // Flags that don't serve us any purpose - } - - // Mark the palette as dirty - _dirtyPalette = true; - - // No alignment necessary. It's always even. - } else if (nextTag == ID_JUNK) { - runHandle(ID_JUNK); - } else - error ("Tag = \'%s\'", tag2str(nextTag)); - - return NULL; -} - -Codec *AVIPlayer::createCodec() { - switch (_vidsHeader.streamHandler) { - case ID_CRAM: - case ID_MSVC: - case ID_WHAM: - return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); - default: - warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); - } - - return NULL; -} - -Audio::AppendableAudioStream *AVIPlayer::createAudioStream() { - if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) - return Audio::makeAppendableAudioStream(AUDIO_RATE, Audio::Mixer::FLAG_UNSIGNED|Audio::Mixer::FLAG_AUTOFREE); - - if (_wvInfo.tag != 0) // No sound - warning ("Unsupported AVI audio format %d", _wvInfo.tag); - - return NULL; -} - -byte AVIPlayer::char2num(char c) { - return (c >= 48 && c <= 57) ? c - 48 : 0; -} - -byte AVIPlayer::getStreamNum(uint32 tag) { - return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); -} - -uint16 AVIPlayer::getStreamType(uint32 tag) { - return tag & 0xffff; -} - -} // End of namespace JMP diff --git a/graphics/video/codecs/codec.h b/graphics/video/codecs/codec.h new file mode 100644 index 0000000000..2b8fd796dd --- /dev/null +++ b/graphics/video/codecs/codec.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_CODEC_H +#define GRAPHICS_CODEC_H + +#include "common/stream.h" +#include "graphics/surface.h" + +namespace Graphics { + +class Codec { +public: + Codec() {} + virtual ~Codec() {} + virtual Surface *decodeImage(Common::SeekableReadStream *stream) = 0; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/video/msvideo1.cpp b/graphics/video/codecs/msvideo1.cpp index 9e57f9a4ab..3717487c4f 100644 --- a/graphics/video/msvideo1.cpp +++ b/graphics/video/codecs/msvideo1.cpp @@ -25,7 +25,7 @@ // Based off ffmpeg's msvideo.cpp -#include "graphics/video/msvideo1.h" +#include "graphics/video/codecs/msvideo1.h" namespace Graphics { diff --git a/graphics/video/msvideo1.h b/graphics/video/codecs/msvideo1.h index 676bc72c9f..7950ceb1f1 100644 --- a/graphics/video/msvideo1.h +++ b/graphics/video/codecs/msvideo1.h @@ -26,8 +26,7 @@ #ifndef GRAPHICS_MSVIDEO1_H #define GRAPHICS_MSVIDEO1_H -#include "graphics/video/avi_player.h" -#include "graphics/surface.h" +#include "graphics/video/codecs/codec.h" namespace Graphics { @@ -47,6 +46,6 @@ private: //void decode16(Common::SeekableReadStream *stream); }; -} +} // End of namespace Graphics #endif |