mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-13 20:42:24 +00:00
273 lines
9.1 KiB
C++
273 lines
9.1 KiB
C++
// 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 <fstream>
|
|
#include <cstring>
|
|
#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;
|
|
} |