// This file is a part of Mesen // It is a heavily modified version of the hardware.h/cpp file found in DOSBox's code. /* * Copyright (C) 2002-2011 The DOSBox Team * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdafx.h" #include #include #include "AviWriter.h" #include "BaseCodec.h" #include "RawCodec.h" #include "ZmbvCodec.h" #include "CamstudioCodec.h" void AviWriter::WriteAviChunk(const char *tag, uint32_t size, void *data, uint32_t flags) { uint8_t chunk[8] = { (uint8_t)tag[0], (uint8_t)tag[1], (uint8_t)tag[2], (uint8_t)tag[3] }; host_writed(&chunk[4], size); _file.write((char*)chunk, 8); uint32_t writesize = (size + 1)&~1; _file.write((char*)data, writesize); uint32_t pos = _written + 4; _written += writesize + 8; _aviIndex.push_back(tag[0]); _aviIndex.push_back(tag[1]); _aviIndex.push_back(tag[2]); _aviIndex.push_back(tag[3]); _aviIndex.insert(_aviIndex.end(), 12, 0); host_writed(_aviIndex.data() + _aviIndex.size() - 12, flags); host_writed(_aviIndex.data() + _aviIndex.size() - 8, pos); host_writed(_aviIndex.data() + _aviIndex.size() - 4, size); } void AviWriter::host_writew(uint8_t* buffer, uint16_t value) { buffer[0] = value & 0xFF; buffer[1] = value >> 8; } void AviWriter::host_writed(uint8_t* buffer, uint32_t value) { buffer[0] = value; buffer[1] = value >> 8; buffer[2] = value >> 16; buffer[3] = value >> 24; } bool AviWriter::StartWrite(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate, uint32_t compressionLevel) { _codecType = codec; _file.open(filename, std::ios::out | std::ios::binary); if(!_file) { return false; } switch(_codecType) { default: case VideoCodec::None: _codec.reset(new RawCodec()); break; case VideoCodec::ZMBV: _codec.reset(new ZmbvCodec()); break; case VideoCodec::CSCD: _codec.reset(new CamstudioCodec()); break; } if(!_codec->SetupCompress(width, height, compressionLevel)) { return false; } _frameBuffer = new uint8_t[width*height*bpp]; _aviIndex.clear(); _aviIndex.insert(_aviIndex.end(), 8, 0); _width = width; _height = height; _bpp = bpp; _fps = fps; _audiorate = audioSampleRate; for(int i = 0; i < AviWriter::AviHeaderSize; i++) { _file.put(0); } _frames = 0; _written = 0; _audioPos = 0; _audiowritten = 0; return true; } void AviWriter::EndWrite() { /* Close the video */ uint8_t avi_header[AviWriter::AviHeaderSize]; uint32_t main_list; uint32_t header_pos = 0; #define AVIOUT4(_S_) memcpy(&avi_header[header_pos],_S_,4);header_pos+=4; #define AVIOUTw(_S_) host_writew(&avi_header[header_pos], _S_);header_pos+=2; #define AVIOUTd(_S_) host_writed(&avi_header[header_pos], _S_);header_pos+=4; /* Try and write an avi header */ AVIOUT4("RIFF"); // Riff header AVIOUTd(AviWriter::AviHeaderSize + _written - 8 + (uint32_t)_aviIndex.size()); AVIOUT4("AVI "); AVIOUT4("LIST"); // List header main_list = header_pos; AVIOUTd(0); // TODO size of list AVIOUT4("hdrl"); AVIOUT4("avih"); AVIOUTd(56); /* # of bytes to follow */ AVIOUTd((uint32_t)(1000000 / _fps)); /* Microseconds per frame */ AVIOUTd(0); AVIOUTd(0); /* PaddingGranularity (whatever that might be) */ AVIOUTd(0x110); /* Flags,0x10 has index, 0x100 interleaved */ AVIOUTd(_frames); /* TotalFrames */ AVIOUTd(0); /* InitialFrames */ AVIOUTd(2); /* Stream count */ AVIOUTd(0); /* SuggestedBufferSize */ AVIOUTd(_width); /* Width */ AVIOUTd(_height); /* Height */ AVIOUTd(0); /* TimeScale: Unit used to measure time */ AVIOUTd(0); /* DataRate: Data rate of playback */ AVIOUTd(0); /* StartTime: Starting time of AVI data */ AVIOUTd(0); /* DataLength: Size of AVI data chunk */ /* Video stream list */ AVIOUT4("LIST"); AVIOUTd(4 + 8 + 56 + 8 + 40); /* Size of the list */ AVIOUT4("strl"); /* video stream header */ AVIOUT4("strh"); AVIOUTd(56); /* # of bytes to follow */ AVIOUT4("vids"); /* Type */ AVIOUT4(_codec->GetFourCC()); /* Handler */ AVIOUTd(0); /* Flags */ AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */ AVIOUTd(0); /* InitialFrames */ AVIOUTd(1000000); /* Scale */ AVIOUTd(_fps); /* Rate: Rate/Scale == samples/second */ AVIOUTd(0); /* Start */ AVIOUTd(_frames); /* Length */ AVIOUTd(0); /* SuggestedBufferSize */ AVIOUTd(~0); /* Quality */ AVIOUTd(0); /* SampleSize */ AVIOUTd(0); /* Frame */ AVIOUTd(0); /* Frame */ /* The video stream format */ AVIOUT4("strf"); AVIOUTd(40); /* # of bytes to follow */ AVIOUTd(40); /* Size */ AVIOUTd(_width); /* Width */ AVIOUTd(_height); /* Height */ // OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ AVIOUTw(1); //number of planes AVIOUTw(24); //bits for colors AVIOUT4(_codec->GetFourCC()); /* Compression */ AVIOUTd(_width * _height * 4); /* SizeImage (in bytes?) */ AVIOUTd(0); /* XPelsPerMeter */ AVIOUTd(0); /* YPelsPerMeter */ AVIOUTd(0); /* ClrUsed: Number of colors used */ AVIOUTd(0); /* ClrImportant: Number of colors important */ /* Audio stream list */ AVIOUT4("LIST"); AVIOUTd(4 + 8 + 56 + 8 + 16); /* Length of list in bytes */ AVIOUT4("strl"); /* The audio stream header */ AVIOUT4("strh"); AVIOUTd(56); /* # of bytes to follow */ AVIOUT4("auds"); AVIOUTd(0); /* Format (Optionally) */ AVIOUTd(0); /* Flags */ AVIOUTd(0); /* Reserved, MS says: wPriority, wLanguage */ AVIOUTd(0); /* InitialFrames */ AVIOUTd(4); /* Scale */ AVIOUTd(_audiorate * 4); /* Rate, actual rate is scale/rate */ AVIOUTd(0); /* Start */ if(!_audiorate) _audiorate = 1; AVIOUTd(_audiowritten / 4); /* Length */ AVIOUTd(0); /* SuggestedBufferSize */ AVIOUTd(~0); /* Quality */ AVIOUTd(4); /* SampleSize */ AVIOUTd(0); /* Frame */ AVIOUTd(0); /* Frame */ /* The audio stream format */ AVIOUT4("strf"); AVIOUTd(16); /* # of bytes to follow */ AVIOUTw(1); /* Format, WAVE_ZMBV_FORMAT_PCM */ AVIOUTw(2); /* Number of channels */ AVIOUTd(_audiorate); /* SamplesPerSec */ AVIOUTd(_audiorate * 4); /* AvgBytesPerSec*/ AVIOUTw(4); /* BlockAlign */ AVIOUTw(16); /* BitsPerSample */ int nmain = header_pos - main_list - 4; /* Finish stream list, i.e. put number of bytes in the list to proper pos */ int njunk = AviWriter::AviHeaderSize - 8 - 12 - header_pos; AVIOUT4("JUNK"); AVIOUTd(njunk); /* Fix the size of the main list */ header_pos = main_list; AVIOUTd(nmain); header_pos = AviWriter::AviHeaderSize - 12; AVIOUT4("LIST"); AVIOUTd(_written + 4); /* Length of list in bytes */ AVIOUT4("movi"); /* First add the index table to the end */ memcpy(_aviIndex.data(), "idx1", 4); host_writed(_aviIndex.data() + 4, (uint32_t)_aviIndex.size() - 8); _file.write((char*)_aviIndex.data(), _aviIndex.size()); _file.seekp(std::ios::beg); _file.write((char*)avi_header, AviWriter::AviHeaderSize); _file.close(); } void AviWriter::AddFrame(uint8_t *frameData) { if(!_file) { return; } bool isKeyFrame = (_frames % 120 == 0) ? 1 : 0; uint8_t* compressedData = nullptr; int written = _codec->CompressFrame(isKeyFrame, frameData, &compressedData); if(written < 0) { return; } if(_codecType == VideoCodec::None) { isKeyFrame = true; } WriteAviChunk(_codecType == VideoCodec::None ? "00db" : "00dc", written, compressedData, isKeyFrame ? 0x10 : 0); _frames++; if(_audioPos) { auto lock = _audioLock.AcquireSafe(); WriteAviChunk("01wb", _audioPos, _audiobuf, 0); _audiowritten += _audioPos; _audioPos = 0; } } void AviWriter::AddSound(int16_t *data, uint32_t sampleCount) { if(!_file) { return; } auto lock = _audioLock.AcquireSafe(); memcpy(_audiobuf+_audioPos/2, data, sampleCount * 4); _audioPos += sampleCount * 4; }