This source file includes following definitions.
- fourccToString
- m_f
- m_f
- isOpened
- open
- close
- read
- seekg
- tellg
- m_is_indx_present
- getFramesCount
- getFrames
- getWidth
- getHeight
- getFps
- printError
- printError
- parseMovi
- parseInfo
- parseIndex
- parseStrl
- skipJunk
- skipJunk
- parseHdrlList
- parseAviWithFrameList
- parseAvi
- parseAvi
- getCaptureDomain
- getFramePos
- setProperty
- getProperty
- readFrame
- grabFrame
- retrieveFrame
- isOpened
- close
- open
- parseRiff
- createMotionJpegCapture
#include "precomp.hpp"
#include <deque>
#include <stdint.h>
namespace cv
{
const uint32_t RIFF_CC = CV_FOURCC('R','I','F','F');
const uint32_t LIST_CC = CV_FOURCC('L','I','S','T');
const uint32_t HDRL_CC = CV_FOURCC('h','d','r','l');
const uint32_t AVIH_CC = CV_FOURCC('a','v','i','h');
const uint32_t STRL_CC = CV_FOURCC('s','t','r','l');
const uint32_t STRH_CC = CV_FOURCC('s','t','r','h');
const uint32_t VIDS_CC = CV_FOURCC('v','i','d','s');
const uint32_t MJPG_CC = CV_FOURCC('M','J','P','G');
const uint32_t MOVI_CC = CV_FOURCC('m','o','v','i');
const uint32_t IDX1_CC = CV_FOURCC('i','d','x','1');
const uint32_t AVI_CC = CV_FOURCC('A','V','I',' ');
const uint32_t AVIX_CC = CV_FOURCC('A','V','I','X');
const uint32_t JUNK_CC = CV_FOURCC('J','U','N','K');
const uint32_t INFO_CC = CV_FOURCC('I','N','F','O');
String fourccToString(uint32_t fourcc);
String fourccToString(uint32_t fourcc)
{
return format("%c%c%c%c", fourcc & 255, (fourcc >> 8) & 255, (fourcc >> 16) & 255, (fourcc >> 24) & 255);
}
#ifndef DWORD
typedef uint32_t DWORD;
#endif
#ifndef WORD
typedef uint16_t WORD;
#endif
#ifndef LONG
typedef int32_t LONG;
#endif
#pragma pack(push, 1)
struct AviMainHeader
{
DWORD dwMicroSecPerFrame;
DWORD dwMaxBytesPerSec;
DWORD dwReserved1;
DWORD dwFlags;
DWORD dwTotalFrames;
DWORD dwInitialFrames;
DWORD dwStreams;
DWORD dwSuggestedBufferSize;
DWORD dwWidth;
DWORD dwHeight;
DWORD dwReserved[4];
};
struct AviStreamHeader
{
uint32_t fccType;
uint32_t fccHandler;
DWORD dwFlags;
DWORD dwPriority;
DWORD dwInitialFrames;
DWORD dwScale;
DWORD dwRate;
DWORD dwStart;
DWORD dwLength;
DWORD dwSuggestedBufferSize;
DWORD dwQuality;
DWORD dwSampleSize;
struct {
short int left;
short int top;
short int right;
short int bottom;
} rcFrame;
};
struct AviIndex
{
DWORD ckid;
DWORD dwFlags;
DWORD dwChunkOffset;
DWORD dwChunkLength;
};
struct BitmapInfoHeader
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
struct RiffChunk
{
uint32_t m_four_cc;
uint32_t m_size;
};
struct RiffList
{
uint32_t m_riff_or_list_cc;
uint32_t m_size;
uint32_t m_list_type_cc;
};
#pragma pack(pop)
class MjpegInputStream
{
public:
MjpegInputStream();
MjpegInputStream(const String& filename);
~MjpegInputStream();
MjpegInputStream& read(char*, uint64_t);
MjpegInputStream& seekg(uint64_t);
uint64_t tellg();
bool isOpened() const;
bool open(const String& filename);
void close();
operator bool();
private:
bool m_is_valid;
FILE* m_f;
};
MjpegInputStream::MjpegInputStream(): m_is_valid(false), m_f(0)
{
}
MjpegInputStream::MjpegInputStream(const String& filename): m_is_valid(false), m_f(0)
{
open(filename);
}
bool MjpegInputStream::isOpened() const
{
return m_f != 0;
}
bool MjpegInputStream::open(const String& filename)
{
close();
m_f = fopen(filename.c_str(), "rb");
m_is_valid = isOpened();
return m_is_valid;
}
void MjpegInputStream::close()
{
if(isOpened())
{
m_is_valid = false;
fclose(m_f);
m_f = 0;
}
}
MjpegInputStream& MjpegInputStream::read(char* buf, uint64_t count)
{
if(isOpened())
{
m_is_valid = (count == fread((void*)buf, 1, (size_t)count, m_f));
}
return *this;
}
MjpegInputStream& MjpegInputStream::seekg(uint64_t pos)
{
m_is_valid = (fseek(m_f, (long)pos, SEEK_SET) == 0);
return *this;
}
uint64_t MjpegInputStream::tellg()
{
return ftell(m_f);
}
MjpegInputStream::operator bool()
{
return m_is_valid;
}
MjpegInputStream::~MjpegInputStream()
{
close();
}
MjpegInputStream& operator >> (MjpegInputStream& is, AviMainHeader& avih);
MjpegInputStream& operator >> (MjpegInputStream& is, AviStreamHeader& strh);
MjpegInputStream& operator >> (MjpegInputStream& is, BitmapInfoHeader& bmph);
MjpegInputStream& operator >> (MjpegInputStream& is, RiffList& riff_list);
MjpegInputStream& operator >> (MjpegInputStream& is, RiffChunk& riff_chunk);
MjpegInputStream& operator >> (MjpegInputStream& is, AviIndex& idx1);
MjpegInputStream& operator >> (MjpegInputStream& is, AviMainHeader& avih)
{
is.read((char*)(&avih), sizeof(AviMainHeader));
return is;
}
MjpegInputStream& operator >> (MjpegInputStream& is, AviStreamHeader& strh)
{
is.read((char*)(&strh), sizeof(AviStreamHeader));
return is;
}
MjpegInputStream& operator >> (MjpegInputStream& is, BitmapInfoHeader& bmph)
{
is.read((char*)(&bmph), sizeof(BitmapInfoHeader));
return is;
}
MjpegInputStream& operator >> (MjpegInputStream& is, RiffList& riff_list)
{
is.read((char*)(&riff_list), sizeof(riff_list));
return is;
}
MjpegInputStream& operator >> (MjpegInputStream& is, RiffChunk& riff_chunk)
{
is.read((char*)(&riff_chunk), sizeof(riff_chunk));
return is;
}
MjpegInputStream& operator >> (MjpegInputStream& is, AviIndex& idx1)
{
is.read((char*)(&idx1), sizeof(idx1));
return is;
}
typedef std::deque< std::pair<uint64_t, uint32_t> > frame_list;
typedef frame_list::iterator frame_iterator;
class AviMjpegStream
{
public:
AviMjpegStream();
bool parseAvi(MjpegInputStream& in_str);
bool parseAvi(MjpegInputStream& in_str, frame_list& in_frame_list);
size_t getFramesCount();
frame_list& getFrames();
uint32_t getWidth();
uint32_t getHeight();
double getFps();
protected:
bool parseAviWithFrameList(MjpegInputStream& in_str, frame_list& in_frame_list);
void skipJunk(RiffChunk& chunk, MjpegInputStream& in_str);
void skipJunk(RiffList& list, MjpegInputStream& in_str);
bool parseHdrlList(MjpegInputStream& in_str);
bool parseIndex(MjpegInputStream& in_str, uint32_t index_size, frame_list& in_frame_list);
bool parseMovi(MjpegInputStream& in_str, frame_list& in_frame_list);
bool parseStrl(MjpegInputStream& in_str, uint8_t stream_id);
bool parseInfo(MjpegInputStream& in_str);
void printError(MjpegInputStream& in_str, RiffList& list, uint32_t expected_fourcc);
void printError(MjpegInputStream& in_str, RiffChunk& chunk, uint32_t expected_fourcc);
uint32_t m_stream_id;
uint64_t m_movi_start;
uint64_t m_movi_end;
frame_list m_frame_list;
uint32_t m_width;
uint32_t m_height;
double m_fps;
bool m_is_indx_present;
};
AviMjpegStream::AviMjpegStream(): m_stream_id(0), m_movi_start(0), m_movi_end(0), m_width(0), m_height(0), m_fps(0), m_is_indx_present(false)
{
}
size_t AviMjpegStream::getFramesCount()
{
return m_frame_list.size();
}
frame_list& AviMjpegStream::getFrames()
{
return m_frame_list;
}
uint32_t AviMjpegStream::getWidth()
{
return m_width;
}
uint32_t AviMjpegStream::getHeight()
{
return m_height;
}
double AviMjpegStream::getFps()
{
return m_fps;
}
void AviMjpegStream::printError(MjpegInputStream& in_str, RiffList& list, uint32_t expected_fourcc)
{
if(!in_str)
{
fprintf(stderr, "Unexpected end of file while searching for %s list\n", fourccToString(expected_fourcc).c_str());
}
else if(list.m_riff_or_list_cc != LIST_CC)
{
fprintf(stderr, "Unexpected element. Expected: %s. Got: %s.\n", fourccToString(LIST_CC).c_str(), fourccToString(list.m_riff_or_list_cc).c_str());
}
else
{
fprintf(stderr, "Unexpected list type. Expected: %s. Got: %s.\n", fourccToString(expected_fourcc).c_str(), fourccToString(list.m_list_type_cc).c_str());
}
}
void AviMjpegStream::printError(MjpegInputStream& in_str, RiffChunk& chunk, uint32_t expected_fourcc)
{
if(!in_str)
{
fprintf(stderr, "Unexpected end of file while searching for %s chunk\n", fourccToString(expected_fourcc).c_str());
}
else
{
fprintf(stderr, "Unexpected element. Expected: %s. Got: %s.\n", fourccToString(expected_fourcc).c_str(), fourccToString(chunk.m_four_cc).c_str());
}
}
bool AviMjpegStream::parseMovi(MjpegInputStream&, frame_list&)
{
return true;
}
bool AviMjpegStream::parseInfo(MjpegInputStream&)
{
return true;
}
bool AviMjpegStream::parseIndex(MjpegInputStream& in_str, uint32_t index_size, frame_list& in_frame_list)
{
uint64_t index_end = in_str.tellg();
index_end += index_size;
bool result = false;
while(in_str && (in_str.tellg() < index_end))
{
AviIndex idx1;
in_str >> idx1;
if(idx1.ckid == m_stream_id)
{
uint64_t absolute_pos = m_movi_start + idx1.dwChunkOffset;
if(absolute_pos < m_movi_end)
{
in_frame_list.push_back(std::make_pair(absolute_pos, idx1.dwChunkLength));
}
else
{
fprintf(stderr, "Frame offset points outside movi section.\n");
}
}
result = true;
}
return result;
}
bool AviMjpegStream::parseStrl(MjpegInputStream& in_str, uint8_t stream_id)
{
RiffChunk strh;
in_str >> strh;
if(in_str && strh.m_four_cc == STRH_CC)
{
uint64_t next_strl_list = in_str.tellg();
next_strl_list += strh.m_size;
AviStreamHeader strm_hdr;
in_str >> strm_hdr;
if(strm_hdr.fccType == VIDS_CC && strm_hdr.fccHandler == MJPG_CC)
{
uint8_t first_digit = (stream_id/10) + '0';
uint8_t second_digit = (stream_id%10) + '0';
if(m_stream_id == 0)
{
m_stream_id = CV_FOURCC(first_digit, second_digit, 'd', 'c');
m_fps = double(strm_hdr.dwRate)/strm_hdr.dwScale;
}
else
{
fprintf(stderr, "More than one video stream found within AVI/AVIX list. Stream %c%cdc would be ignored\n", first_digit, second_digit);
}
return true;
}
}
return false;
}
void AviMjpegStream::skipJunk(RiffChunk& chunk, MjpegInputStream& in_str)
{
if(chunk.m_four_cc == JUNK_CC)
{
in_str.seekg(in_str.tellg() + chunk.m_size);
in_str >> chunk;
}
}
void AviMjpegStream::skipJunk(RiffList& list, MjpegInputStream& in_str)
{
if(list.m_riff_or_list_cc == JUNK_CC)
{
in_str.seekg(in_str.tellg() + list.m_size - 4);
in_str >> list;
}
}
bool AviMjpegStream::parseHdrlList(MjpegInputStream& in_str)
{
bool result = false;
RiffChunk avih;
in_str >> avih;
if(in_str && avih.m_four_cc == AVIH_CC)
{
uint64_t next_strl_list = in_str.tellg();
next_strl_list += avih.m_size;
AviMainHeader avi_hdr;
in_str >> avi_hdr;
if(in_str)
{
m_is_indx_present = ((avi_hdr.dwFlags & 0x10) != 0);
DWORD number_of_streams = avi_hdr.dwStreams;
m_width = avi_hdr.dwWidth;
m_height = avi_hdr.dwWidth;
for(DWORD i = 0; i < number_of_streams; ++i)
{
in_str.seekg(next_strl_list);
RiffList strl_list;
in_str >> strl_list;
if( in_str && strl_list.m_riff_or_list_cc == LIST_CC && strl_list.m_list_type_cc == STRL_CC )
{
next_strl_list = in_str.tellg();
next_strl_list += (strl_list.m_size - 4);
result = parseStrl(in_str, (uint8_t)i);
}
else
{
printError(in_str, strl_list, STRL_CC);
}
}
}
}
else
{
printError(in_str, avih, AVIH_CC);
}
return result;
}
bool AviMjpegStream::parseAviWithFrameList(MjpegInputStream& in_str, frame_list& in_frame_list)
{
RiffList hdrl_list;
in_str >> hdrl_list;
if( in_str && hdrl_list.m_riff_or_list_cc == LIST_CC && hdrl_list.m_list_type_cc == HDRL_CC )
{
uint64_t next_list = in_str.tellg();
next_list += (hdrl_list.m_size - 4);
if(parseHdrlList(in_str))
{
in_str.seekg(next_list);
RiffList some_list;
in_str >> some_list;
if(in_str && some_list.m_riff_or_list_cc == LIST_CC && some_list.m_list_type_cc == INFO_CC)
{
next_list = in_str.tellg();
next_list += (some_list.m_size - 4);
parseInfo(in_str);
in_str.seekg(next_list);
in_str >> some_list;
}
skipJunk(some_list, in_str);
if(in_str && some_list.m_riff_or_list_cc == LIST_CC && some_list.m_list_type_cc == MOVI_CC)
{
bool is_index_found = false;
m_movi_start = in_str.tellg();
m_movi_start -= 4;
m_movi_end = m_movi_start + some_list.m_size;
if(m_is_indx_present)
{
uint32_t indx_pos = (uint32_t)m_movi_start + 4;
indx_pos += (some_list.m_size - 4);
in_str.seekg(indx_pos);
RiffChunk index_chunk;
in_str >> index_chunk;
if(in_str && index_chunk.m_four_cc == IDX1_CC)
{
is_index_found = parseIndex(in_str, index_chunk.m_size, in_frame_list);
}
else
{
printError(in_str, index_chunk, IDX1_CC);
}
}
if(!is_index_found)
{
parseMovi(in_str, in_frame_list);
fprintf(stderr, "Failed to parse avi: index was not found\n");
}
}
else
{
printError(in_str, some_list, MOVI_CC);
}
}
}
else
{
printError(in_str, hdrl_list, HDRL_CC);
}
return in_frame_list.size() > 0;
}
bool AviMjpegStream::parseAvi(MjpegInputStream& in_str, frame_list& in_frame_list)
{
return parseAviWithFrameList(in_str, in_frame_list);
}
bool AviMjpegStream::parseAvi(MjpegInputStream& in_str)
{
return parseAviWithFrameList(in_str, m_frame_list);
}
class MotionJpegCapture: public IVideoCapture
{
public:
virtual ~MotionJpegCapture();
virtual double getProperty(int) const;
virtual bool setProperty(int, double);
virtual bool grabFrame();
virtual bool retrieveFrame(int, OutputArray);
virtual bool isOpened() const;
virtual int getCaptureDomain() { return CAP_ANY; }
MotionJpegCapture(const String&);
bool open(const String&);
void close();
protected:
bool parseRiff(MjpegInputStream& in_str);
inline uint64_t getFramePos() const;
std::vector<char> readFrame(frame_iterator it);
MjpegInputStream m_file_stream;
bool m_is_first_frame;
frame_list m_mjpeg_frames;
frame_iterator m_frame_iterator;
Mat m_current_frame;
uint32_t m_frame_width;
uint32_t m_frame_height;
double m_fps;
};
uint64_t MotionJpegCapture::getFramePos() const
{
if(m_is_first_frame)
return 0;
if(m_frame_iterator == m_mjpeg_frames.end())
return m_mjpeg_frames.size();
return m_frame_iterator - m_mjpeg_frames.begin() + 1;
}
bool MotionJpegCapture::setProperty(int property, double value)
{
if(property == CAP_PROP_POS_FRAMES)
{
if(int(value) == 0)
{
m_is_first_frame = true;
m_frame_iterator = m_mjpeg_frames.end();
return true;
}
else if(m_mjpeg_frames.size() > value)
{
m_frame_iterator = m_mjpeg_frames.begin() + int(value - 1);
m_is_first_frame = false;
return true;
}
}
return false;
}
double MotionJpegCapture::getProperty(int property) const
{
switch(property)
{
case CAP_PROP_POS_FRAMES:
return (double)getFramePos();
case CAP_PROP_POS_AVI_RATIO:
return double(getFramePos())/m_mjpeg_frames.size();
case CAP_PROP_FRAME_WIDTH:
return (double)m_frame_width;
case CAP_PROP_FRAME_HEIGHT:
return (double)m_frame_height;
case CAP_PROP_FPS:
return m_fps;
case CAP_PROP_FOURCC:
return (double)CV_FOURCC('M','J','P','G');
case CAP_PROP_FRAME_COUNT:
return (double)m_mjpeg_frames.size();
case CAP_PROP_FORMAT:
return 0;
default:
return 0;
}
}
std::vector<char> MotionJpegCapture::readFrame(frame_iterator it)
{
m_file_stream.seekg(it->first);
RiffChunk chunk;
m_file_stream >> chunk;
std::vector<char> result;
result.reserve(chunk.m_size);
result.resize(chunk.m_size);
m_file_stream.read(result.data(), chunk.m_size);
return result;
}
bool MotionJpegCapture::grabFrame()
{
if(isOpened())
{
if(m_is_first_frame)
{
m_is_first_frame = false;
m_frame_iterator = m_mjpeg_frames.begin();
}
else
{
++m_frame_iterator;
}
}
return m_frame_iterator != m_mjpeg_frames.end();
}
bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
{
if(m_frame_iterator != m_mjpeg_frames.end())
{
std::vector<char> data = readFrame(m_frame_iterator);
if(data.size())
{
m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR);
}
m_current_frame.copyTo(output_frame);
return true;
}
return false;
}
MotionJpegCapture::~MotionJpegCapture()
{
close();
}
MotionJpegCapture::MotionJpegCapture(const String& filename)
{
open(filename);
}
bool MotionJpegCapture::isOpened() const
{
return m_mjpeg_frames.size() > 0;
}
void MotionJpegCapture::close()
{
m_file_stream.close();
m_frame_iterator = m_mjpeg_frames.end();
}
bool MotionJpegCapture::open(const String& filename)
{
close();
m_file_stream.open(filename);
m_frame_iterator = m_mjpeg_frames.end();
m_is_first_frame = true;
if(!parseRiff(m_file_stream))
{
close();
}
return isOpened();
}
bool MotionJpegCapture::parseRiff(MjpegInputStream& in_str)
{
bool result = false;
while(in_str)
{
RiffList riff_list;
in_str >> riff_list;
if( in_str && riff_list.m_riff_or_list_cc == RIFF_CC &&
((riff_list.m_list_type_cc == AVI_CC) | (riff_list.m_list_type_cc == AVIX_CC)) )
{
uint64_t next_riff = in_str.tellg();
next_riff += (riff_list.m_size - 4);
AviMjpegStream mjpeg_video_stream;
bool is_parsed = mjpeg_video_stream.parseAvi(in_str, m_mjpeg_frames);
result = result || is_parsed;
if(is_parsed)
{
m_frame_width = mjpeg_video_stream.getWidth();
m_frame_height = mjpeg_video_stream.getHeight();
m_fps = mjpeg_video_stream.getFps();
}
in_str.seekg(next_riff);
}
else
{
break;
}
}
return result;
}
Ptr<IVideoCapture> createMotionJpegCapture(const String& filename)
{
Ptr<MotionJpegCapture> mjdecoder(new MotionJpegCapture(filename));
if( mjdecoder->isOpened() )
return mjdecoder;
return Ptr<MotionJpegCapture>();
}
}