#if !defined(NSV_READER_HPP) #define NSV_READER_HPP //______________________________________________________________________________ // // NSV_Reader.hpp // NSV Reader Class #include "NSV.hpp" #include "endian.hpp" #include #include #include #include #include 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 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 basic_Reader::basic_Reader(File& f) : m_file(f), m_strFile(), m_fileHeader(), m_frameHeader(), m_bFrameHeader(false), m_nFrame(0) { } //-------------------------------------- template basic_Reader::~basic_Reader() { close(); } //-------------------------------------- template void basic_Reader::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 void basic_Reader::close() { if (m_ifs) { m_ifs.close(); } m_strFile.erase(); return; } //-------------------------------------- template File& basic_Reader::file() { return m_file; } //-------------------------------------- template const std::string& basic_Reader::fileName() const { return m_strFile; } //-------------------------------------- template void basic_Reader::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 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(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(m_file.rateDenom()) * 1000; INT64 nFrames = (static_cast(m_fileHeader.m_iFileSize_ms) * static_cast(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 void basic_Reader::readFrame() { readFrameHeader(); readPayload(); return; } //-------------------------------------- template void basic_Reader::readFrameInfo() { readFrameHeader(); readPayloadInfo(); return; } //-------------------------------------- template void basic_Reader::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(&m_frameHeader.m_fccVideo), 4); m_ifs.read(reinterpret_cast(&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(&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 void basic_Reader::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(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(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(sAudio.data()), iAudio); sAudio.keyFrame(true); } else { m_ifs.seekg(iAudio, IOS_BASE::cur); } return; } //-------------------------------------- template void basic_Reader::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 void basic_Reader::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(uh) << 4) | ((c >> 4) & 0xf); iAudio = uhAudio; return; } //-------------------------------------- template void basic_Reader::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(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 INT64 basic_Reader::frames() const { return m_file.frames(); } //-------------------------------------- template INT64 basic_Reader::frame() const { return m_nFrame; } //-------------------------------------- template void basic_Reader::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 void basic_Reader::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 bool basic_Reader::eof() { return m_ifs.tellg() >= m_fileHeader.m_sizeFile; } //-------------------------------------- template const FileHeader& basic_Reader::fileHeader() const { return m_fileHeader; } //-------------------------------------- template const FrameHeader& basic_Reader::frameHeader() const { return m_frameHeader; } //-------------------------------------- template void* basic_Reader::get_ifs() { return &m_ifs; } //-------------------------------------- template short basic_Reader::read_i16() { assert(m_ifs); short i16; m_ifs.read(reinterpret_cast(&i16), 2); return native_endian(i16, false); } //-------------------------------------- template unsigned short basic_Reader::read_ui16() { assert(m_ifs); unsigned short ui16; m_ifs.read(reinterpret_cast(&ui16), 2); return native_endian(ui16, false); } //-------------------------------------- template int basic_Reader::read_i32() { assert(m_ifs); int i32; m_ifs.read(reinterpret_cast(&i32), 4); return native_endian(i32, false); } //-------------------------------------- template unsigned int basic_Reader::read_ui32() { assert(m_ifs); unsigned int ui32; m_ifs.read(reinterpret_cast(&ui32), 4); return native_endian(ui32, false); } } // namespace NSV #endif // NSV_READER_HPP