winamp/Src/libvp6/include/NSV_Reader.hpp

689 lines
18 KiB
C++
Raw Normal View History

2024-09-24 13:54:57 +01:00
#if !defined(NSV_READER_HPP)
#define NSV_READER_HPP
//______________________________________________________________________________
//
// NSV_Reader.hpp
// NSV Reader Class
#include "NSV.hpp"
#include "endian.hpp"
#include <string>
#include <memory>
#include <fstream>
#include <sstream>
#include <cassert>
namespace NSV
{
//--------------------------------------
// Defines the interface for the basic_Reader template instantiations
class Reader_base
{
public:
virtual ~Reader_base()
{
}
virtual void open(const std::string& strFile) = 0;
virtual void close() = 0;
virtual File& file() = 0;
virtual const std::string& fileName() const = 0;
virtual void readFileHeader() = 0;
virtual void readFrame() = 0;
virtual void readFrameInfo() = 0;
virtual void readFrameHeader() = 0;
virtual void readPayload() = 0;
virtual void readPayloadInfo() = 0;
virtual void readPayloadHeader(int& nAux, int& iAuxPlusVideo, int& iAudio) = 0;
virtual void buildIndex(int nIndexEntries) = 0;
virtual INT64 frames() const = 0;
virtual INT64 frame() const = 0;
virtual void seek(INT64 nFrame) = 0;
virtual void readSample(int nStream, unsigned char* pData, size_t sizeDataMax, size_t& sizeData, bool& bKeyFrame) = 0;
virtual bool eof() = 0;
virtual const FileHeader& fileHeader() const = 0;
virtual const FrameHeader& frameHeader() const = 0;
virtual void* get_ifs() = 0;
};
//--------------------------------------
template<typename T>
class basic_Reader : public Reader_base
{
public:
basic_Reader(File& f);
~basic_Reader();
void open(const std::string& strFile);
void close();
File& file();
const std::string& fileName() const;
void readFileHeader();
void readFrame();
void readFrameInfo();
void readFrameHeader();
void readPayload();
void readPayloadInfo();
void readPayloadHeader(int& nAux, int& iAuxPlusVideo, int& iAudio);
void buildIndex(int nIndexEntries = 0); // index all frames by default
INT64 frames() const;
INT64 frame() const;
void seek(INT64 nFrame);
void readSample(int nStream, unsigned char* pData, size_t sizeDataMax, size_t& sizeData, bool& bKeyFrame);
bool eof();
const FileHeader& fileHeader() const;
const FrameHeader& frameHeader() const;
void* get_ifs();
private:
basic_Reader(const basic_Reader& r); // Not implemented
basic_Reader& operator=(const basic_Reader& r); // Not implemented
short read_i16();
unsigned short read_ui16();
int read_i32();
unsigned int read_ui32();
File& m_file;
std::string m_strFile;
T m_ifs;
FileHeader m_fileHeader;
FrameHeader m_frameHeader;
bool m_bFrameHeader;
INT64 m_nFrame;
};
//--------------------------------------
template<typename T>
basic_Reader<T>::basic_Reader(File& f) :
m_file(f),
m_strFile(),
m_fileHeader(),
m_frameHeader(),
m_bFrameHeader(false),
m_nFrame(0)
{
}
//--------------------------------------
template<typename T>
basic_Reader<T>::~basic_Reader()
{
close();
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::open(const std::string& strFile)
{
m_strFile = strFile;
m_ifs.open(m_strFile.c_str(), IOS_BASE::binary);
if (!m_ifs)
{
std::ostringstream ossError;
ossError << "Error opening file " << m_strFile;
throw Exception(ossError.str());
}
readFileHeader();
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::close()
{
if (m_ifs)
{
m_ifs.close();
}
m_strFile.erase();
return;
}
//--------------------------------------
template<typename T>
File& basic_Reader<T>::file()
{
return m_file;
}
//--------------------------------------
template<typename T>
const std::string& basic_Reader<T>::fileName() const
{
return m_strFile;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readFileHeader()
{
assert(m_ifs);
// Read file header signature
char cSignature[5];
m_ifs.read(cSignature, 4);
if (strncmp(cSignature, "NSVf", 4) == 0)
{
cSignature[4] = '\0';
m_fileHeader.m_fccSignature = cSignature;
m_fileHeader.m_sizeHeader = read_i32();
m_fileHeader.m_sizeFile = read_i32();
m_fileHeader.m_iFileSize_ms = read_i32();
m_fileHeader.m_sizeMetaData = read_i32();
m_fileHeader.m_nTOCAlloc = read_i32();
m_fileHeader.m_nTOCSize = read_i32();
if ((m_fileHeader.m_sizeFile > 0 && m_fileHeader.m_sizeFile < m_fileHeader.m_sizeHeader)
|| m_fileHeader.m_nTOCSize > m_fileHeader.m_nTOCAlloc)
{
throw Exception("Invalid NSV file header");
}
if (m_fileHeader.m_sizeMetaData > 0)
{
std::auto_ptr<char> apcMetaData(new char[m_fileHeader.m_sizeMetaData + 1]);
char* pcMetaData = apcMetaData.get();
if (pcMetaData == 0)
{
throw Exception("Out of memory");
}
m_ifs.read(pcMetaData, m_fileHeader.m_sizeMetaData);
pcMetaData[m_fileHeader.m_sizeMetaData] = '\0';
m_file.header(pcMetaData, m_fileHeader.m_nTOCSize);
}
else
{
m_file.header("", m_fileHeader.m_nTOCSize);
}
for (int nEntry = 0; nEntry < m_fileHeader.m_nTOCSize; ++nEntry)
{
POS_TYPE posOffset;
posOffset = read_ui32();
m_file.indexEntry(nEntry, posOffset);
}
m_ifs.ignore((m_fileHeader.m_nTOCAlloc - m_fileHeader.m_nTOCSize) * 4);
if (m_ifs.tellg() > static_cast<POS_TYPE>(m_fileHeader.m_sizeHeader))
{
throw Exception("Invalid NSV file header");
}
m_file.dataOffset(m_fileHeader.m_sizeHeader);
m_ifs.seekg(m_file.dataOffset());
}
else // No file header present
{
m_fileHeader.m_sizeHeader = 0;
m_ifs.seekg(0, IOS_BASE::end);
m_fileHeader.m_sizeFile = m_ifs.tellg();
m_fileHeader.m_iFileSize_ms = 0;
m_fileHeader.m_nTOCAlloc = 0;
m_file.header("", 0);
m_file.dataOffset(0);
m_ifs.seekg(m_file.dataOffset());
}
// Read stream info from first frame header
readFrameHeader();
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
m_file.size(m_frameHeader.m_iWidth, m_frameHeader.m_iHeight);
m_file.frameRate(m_frameHeader.m_iFrameRate);
if (m_fileHeader.m_iFileSize_ms > 0)
{
INT64 nFramesDenom = static_cast<INT64>(m_file.rateDenom()) * 1000;
INT64 nFrames = (static_cast<INT64>(m_fileHeader.m_iFileSize_ms) * static_cast<INT64>(m_file.rateNum()) + nFramesDenom / 2) / nFramesDenom;
m_file.frames(nFrames);
}
// Set up primary video and audio streams
m_file.newStream(m_frameHeader.m_fccVideo);
m_file.newStream(m_frameHeader.m_fccAudio);
m_file.stream(0).rate(m_file.rateNum(), m_file.rateDenom());
m_file.stream(0).samples(m_file.frames());
// Set up aux streams
for (int n = 0; n < nAux; ++n)
{
unsigned short uh = read_ui16();
unsigned long ul = read_ui32();
m_ifs.ignore(uh);
m_file.newStream(FourCC(ul));
// More info ...
}
m_ifs.seekg(m_file.dataOffset());
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readFrame()
{
readFrameHeader();
readPayload();
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readFrameInfo()
{
readFrameHeader();
readPayloadInfo();
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readFrameHeader()
{
assert(m_ifs);
// Read frame header signature
char cSignature[5];
m_ifs.read(cSignature, 2);
if (strncmp(cSignature, "\xef\xbe", 2) == 0)
{
m_frameHeader.m_fccSignature = 0UL;
m_frameHeader.m_bKeyFrame = false;
m_file.syncOffset(0);
return;
}
m_ifs.read(&cSignature[2], 2);
if (strncmp(cSignature, "NSVs", 4) != 0)
{
throw Exception("Invalid NSV frame header");
}
cSignature[4] = '\0';
m_frameHeader.m_fccSignature = cSignature;
m_ifs.read(reinterpret_cast<char*>(&m_frameHeader.m_fccVideo), 4);
m_ifs.read(reinterpret_cast<char*>(&m_frameHeader.m_fccAudio), 4);
m_frameHeader.m_iWidth = read_i16();
m_frameHeader.m_iHeight = read_i16();
unsigned char uc;
m_ifs.read(reinterpret_cast<char*>(&uc), 1);
m_frameHeader.m_iFrameRate = uc;
m_frameHeader.m_iSyncOffset_ms = read_i16();
m_frameHeader.m_bKeyFrame = true;
if (!m_bFrameHeader)
{
// m_file.newStream(m_frameHeader.m_fccVideo);
// m_file.newStream(m_frameHeader.m_fccAudio);
// m_file.size(m_frameHeader.m_iWidth, m_frameHeader.m_iHeight);
// m_file.frameRate(m_frameHeader.m_iFrameRate);
m_bFrameHeader = true;
}
else
{
if ((m_file.streamVideo() >= 0 && m_file.videoFormat() != m_frameHeader.m_fccVideo)
|| (m_file.streamAudio() >= 0 && m_file.audioFormat() != m_frameHeader.m_fccAudio)
|| m_file.width() != m_frameHeader.m_iWidth
|| m_file.height() != m_frameHeader.m_iHeight
|| m_file.frameRate() != m_frameHeader.m_iFrameRate)
{
throw Exception("Invalid NSV frame header");
}
}
m_file.syncOffset(m_frameHeader.m_iSyncOffset_ms);
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readPayload()
{
assert(m_ifs);
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
int iAux = 0;
for (int n = 0; n < nAux; ++n)
{
unsigned short uh = read_ui16();
unsigned int ui = read_ui32();
Stream& s = m_file.stream(m_file.streamAux(n));
s.dataSize(uh);
m_ifs.read(reinterpret_cast<char*>(s.data()), uh);
iAux += uh;
}
if (m_file.streamVideo() >= 0)
{
int iVideo = iAuxPlusVideo - iAux;
Stream& sVideo = m_file.stream(m_file.streamVideo());
sVideo.dataSize(iVideo);
m_ifs.read(reinterpret_cast<char*>(sVideo.data()), iVideo);
sVideo.keyFrame(m_frameHeader.m_bKeyFrame);
}
else
{
m_ifs.seekg(iAuxPlusVideo - iAux, IOS_BASE::cur);
}
if (m_file.streamAudio() >= 0)
{
Stream& sAudio = m_file.stream(m_file.streamAudio());
sAudio.dataSize(iAudio);
m_ifs.read(reinterpret_cast<char*>(sAudio.data()), iAudio);
sAudio.keyFrame(true);
}
else
{
m_ifs.seekg(iAudio, IOS_BASE::cur);
}
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readPayloadInfo()
{
assert(m_ifs);
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
int iAux = 0;
for (int n = 0; n < nAux; ++n)
{
unsigned short uh = read_ui16();
unsigned int ui = read_ui32();
m_ifs.ignore(uh);
Stream& s = m_file.stream(m_file.streamAux(n));
s.dataSize(uh);
iAux += uh;
}
if (m_file.streamVideo() >= 0)
{
int iVideo = iAuxPlusVideo - iAux;
Stream& sVideo = m_file.stream(m_file.streamVideo());
sVideo.dataSize(iVideo);
sVideo.keyFrame(m_frameHeader.m_bKeyFrame);
}
if (m_file.streamAudio() >= 0)
{
Stream& sAudio = m_file.stream(m_file.streamAudio());
sAudio.dataSize(iAudio);
sAudio.keyFrame(true);
}
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readPayloadHeader(int& nAux, int& iAuxPlusVideo, int& iAudio)
{
assert(m_ifs);
char c;
unsigned short uh;
unsigned short uhAudio;
m_ifs.get(c);
uh = read_ui16();
uhAudio = read_ui16();
nAux = c & 0xf;
iAuxPlusVideo = (static_cast<int>(uh) << 4) | ((c >> 4) & 0xf);
iAudio = uhAudio;
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::buildIndex(int nIndexEntries)
{
assert(nIndexEntries == 0); // Only creates full index for now ...
m_file.index().clear();
m_file.frames(0);
m_ifs.seekg(m_file.dataOffset());
INT64 nFrames = 0;
for (; !eof(); ++nFrames)
{
m_file.appendIndexEntry(static_cast<POS_TYPE>(m_ifs.tellg()) - m_file.dataOffset());
readFrameHeader();
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
m_ifs.seekg(iAuxPlusVideo + iAudio, IOS_BASE::cur);
}
m_file.frames(nFrames);
m_ifs.seekg(m_file.dataOffset());
return;
}
//--------------------------------------
template<typename T>
INT64 basic_Reader<T>::frames() const
{
return m_file.frames();
}
//--------------------------------------
template<typename T>
INT64 basic_Reader<T>::frame() const
{
return m_nFrame;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::seek(INT64 nFrame)
{
assert(m_ifs);
INT64 nFrames = m_file.frames();
assert(nFrame < nFrames || nFrames == -1);
int nIndexEntries = m_file.index().size();
if (nIndexEntries > 0)
{
int nIndexEntry = nIndexEntries * nFrame / nFrames;
INT64 nFrameIndex = (nIndexEntry * nFrames + nIndexEntries / 2) / nIndexEntries;
m_ifs.seekg(m_file.dataOffset() + m_file.index()[nIndexEntry].m_posOffset);
for (; nFrameIndex < nFrame; ++nFrameIndex)
{
readFrameHeader();
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
m_ifs.seekg(iAuxPlusVideo + iAudio, IOS_BASE::cur);
}
m_nFrame = nFrame;
}
else
{
m_ifs.seekg(m_file.dataOffset());
for (m_nFrame = 0; m_nFrame < nFrame; ++m_nFrame)
{
readFrameHeader();
int nAux;
int iAuxPlusVideo;
int iAudio;
readPayloadHeader(nAux, iAuxPlusVideo, iAudio);
m_ifs.seekg(iAuxPlusVideo + iAudio, IOS_BASE::cur);
}
assert(m_nFrame == nFrame);
}
return;
}
//--------------------------------------
template<typename T>
void basic_Reader<T>::readSample(int nStream, unsigned char* pData, size_t sizeDataMax, size_t& sizeData, bool& bKeyFrame)
{
assert(m_ifs);
assert(pData != 0);
readFrame();
Stream& s = m_file.stream(nStream);
size_t size = s.dataSize();
if (sizeDataMax < s.dataSize())
{
size = sizeDataMax;
}
memcpy(pData, s.data(), size);
sizeData = s.dataSize();
bKeyFrame = s.keyFrame();
return;
}
//--------------------------------------
template<typename T>
bool basic_Reader<T>::eof()
{
return m_ifs.tellg() >= m_fileHeader.m_sizeFile;
}
//--------------------------------------
template<typename T>
const FileHeader& basic_Reader<T>::fileHeader() const
{
return m_fileHeader;
}
//--------------------------------------
template<typename T>
const FrameHeader& basic_Reader<T>::frameHeader() const
{
return m_frameHeader;
}
//--------------------------------------
template<typename T>
void* basic_Reader<T>::get_ifs()
{
return &m_ifs;
}
//--------------------------------------
template<typename T>
short basic_Reader<T>::read_i16()
{
assert(m_ifs);
short i16;
m_ifs.read(reinterpret_cast<char*>(&i16), 2);
return native_endian(i16, false);
}
//--------------------------------------
template<typename T>
unsigned short basic_Reader<T>::read_ui16()
{
assert(m_ifs);
unsigned short ui16;
m_ifs.read(reinterpret_cast<char*>(&ui16), 2);
return native_endian(ui16, false);
}
//--------------------------------------
template<typename T>
int basic_Reader<T>::read_i32()
{
assert(m_ifs);
int i32;
m_ifs.read(reinterpret_cast<char*>(&i32), 4);
return native_endian(i32, false);
}
//--------------------------------------
template<typename T>
unsigned int basic_Reader<T>::read_ui32()
{
assert(m_ifs);
unsigned int ui32;
m_ifs.read(reinterpret_cast<char*>(&ui32), 4);
return native_endian(ui32, false);
}
} // namespace NSV
#endif // NSV_READER_HPP