From 740ae3581c73417fe3f503b93339a4b45ea55149 Mon Sep 17 00:00:00 2001 From: kalistratovag Date: Mon, 6 Apr 2015 18:19:22 +0300 Subject: [PATCH] Initial commit of avi mjpeg decoder std::streams removed debug code removed whitespaces removed stdint.h added and trailing whitespaces removed trailing whitespaces removed warnings on windows fix warnings on windows fix pt 2 Refactoring performed: AviMjpegStream class introduced. CAP_PROP_FRAME_WIDTH and CAP_PROP_FRAME_HEIGHT properties support added. Windows warnings & uninitialized variable fix Windows warning fix Win32 warning fix --- modules/videoio/src/cap.cpp | 18 + modules/videoio/src/cap_mjpeg_decoder.cpp | 872 +++++++++++++++++++++- 2 files changed, 888 insertions(+), 2 deletions(-) diff --git a/modules/videoio/src/cap.cpp b/modules/videoio/src/cap.cpp index b5a44da3f7..1fedb0b66d 100644 --- a/modules/videoio/src/cap.cpp +++ b/modules/videoio/src/cap.cpp @@ -552,6 +552,20 @@ static Ptr IVideoCapture_create(int index) } +static Ptr IVideoCapture_create(const String& filename) +{ + Ptr capture; + + capture = createMotionJpegCapture(filename); + if (capture && capture->isOpened()) + { + return capture; + } + + // failed open a camera + return Ptr(); +} + static Ptr IVideoWriter_create(const String& filename, int _fourcc, double fps, Size frameSize, bool isColor) { Ptr iwriter; @@ -582,6 +596,10 @@ VideoCapture::~VideoCapture() bool VideoCapture::open(const String& filename) { if (isOpened()) release(); + icap = IVideoCapture_create(filename); + if (!icap.empty()) + return true; + cap.reset(cvCreateFileCapture(filename.c_str())); return isOpened(); } diff --git a/modules/videoio/src/cap_mjpeg_decoder.cpp b/modules/videoio/src/cap_mjpeg_decoder.cpp index 11a86b2ab3..c6eb8f00d2 100644 --- a/modules/videoio/src/cap_mjpeg_decoder.cpp +++ b/modules/videoio/src/cap_mjpeg_decoder.cpp @@ -40,13 +40,881 @@ //M*/ #include "precomp.hpp" +#include +#include namespace cv { -Ptr createMotionJpegCapture(const String&) +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; // The period between video frames + DWORD dwMaxBytesPerSec; // Maximum data rate of the file + DWORD dwReserved1; // 0 + DWORD dwFlags; // 0x10 AVIF_HASINDEX: The AVI file has an idx1 chunk containing an index at the end of the file. + DWORD dwTotalFrames; // Field of the main header specifies the total number of frames of data in file. + DWORD dwInitialFrames; // Is used for interleaved files + DWORD dwStreams; // Specifies the number of streams in the file. + DWORD dwSuggestedBufferSize; // Field specifies the suggested buffer size forreading the file + DWORD dwWidth; // Fields specify the width of the AVIfile in pixels. + DWORD dwHeight; // Fields specify the height of the AVIfile in pixels. + DWORD dwReserved[4]; // 0, 0, 0, 0 +}; + +struct AviStreamHeader +{ + uint32_t fccType; // 'vids', 'auds', 'txts'... + uint32_t fccHandler; // "cvid", "DIB " + DWORD dwFlags; // 0 + DWORD dwPriority; // 0 + DWORD dwInitialFrames; // 0 + DWORD dwScale; // 1 + DWORD dwRate; // Fps (dwRate - frame rate for video streams) + DWORD dwStart; // 0 + DWORD dwLength; // Frames number (playing time of AVI file as defined by scale and rate) + DWORD dwSuggestedBufferSize; // For reading the stream + DWORD dwQuality; // -1 (encoding quality. If set to -1, drivers use the default quality value) + DWORD dwSampleSize; // 0 means that each frame is in its own chunk + struct { + short int left; + short int top; + short int right; + short int bottom; + } rcFrame; // If stream has a different size than dwWidth*dwHeight(unused) +}; + +struct AviIndex +{ + DWORD ckid; + DWORD dwFlags; + DWORD dwChunkOffset; + DWORD dwChunkLength; +}; + +struct BitmapInfoHeader +{ + DWORD biSize; // Write header size of BITMAPINFO header structure + LONG biWidth; // width in pixels + LONG biHeight; // heigth in pixels + WORD biPlanes; // Number of color planes in which the data is stored + WORD biBitCount; // Number of bits per pixel + DWORD biCompression; // Type of compression used (uncompressed: NO_COMPRESSION=0) + DWORD biSizeImage; // Image Buffer. Quicktime needs 3 bytes also for 8-bit png + // (biCompression==NO_COMPRESSION)?0:xDim*yDim*bytesPerPixel; + LONG biXPelsPerMeter; // Horizontal resolution in pixels per meter + LONG biYPelsPerMeter; // Vertical resolution in pixels per meter + DWORD biClrUsed; // 256 (color table size; for 8-bit only) + DWORD biClrImportant; // Specifies that the first x colors of the color table. Are important to the DIB. +}; + +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; +} + +/* +AVI struct: + +RIFF ('AVI ' + LIST ('hdrl' + 'avih'(
) + LIST ('strl' + 'strh'() + 'strf'() + [ 'strd'() ] + [ 'strn'() ] + [ 'indx'() ] + ... + ) + [LIST ('strl' ...)] + [LIST ('strl' ...)] + ... + [LIST ('odml' + 'dmlh'() + ... + ) + ] + ... + ) + [LIST ('INFO' ...)] + [JUNK] + LIST ('movi' + {{xxdb|xxdc|xxpc|xxwb}() | LIST ('rec ' + {xxdb|xxdc|xxpc|xxwb}() + {xxdb|xxdc|xxpc|xxwb}() + ... + ) + ... + } + ... + ) + ['idx1' () ] + ) + + {xxdb|xxdc|xxpc|xxwb} + xx - stream number: 00, 01, 02, ... + db - uncompressed video frame + dc - commpressed video frame + pc - palette change + wb - audio frame + + JUNK section may pad any data section and must be ignored +*/ + +typedef std::deque< std::pair > frame_list; +typedef frame_list::iterator frame_iterator; + +//Represents single MJPEG video stream within single AVI/AVIX entry +//Multiple video streams within single AVI/AVIX entry are not supported +//ODML index is not supported +class AviMjpegStream +{ +public: + AviMjpegStream(); + //stores founded frames in m_frame_list which be accessed via getFrames + bool parseAvi(MjpegInputStream& in_str); + //stores founded frames in in_frame_list. getFrames() would return empty list + 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_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&) +{ + //not implemented + return true; +} + +bool AviMjpegStream::parseInfo(MjpegInputStream&) +{ + //not implemented + 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 + { + //unsupported case + 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 + { + //second mjpeg video stream found which is not supported + 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) + { + //JUNK chunk is 4 bytes less than LIST + 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; + + //the number of strl lists must be equal to number of streams specified in main avi header + 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(); + //RiffList::m_size includes fourCC field which we have already read + 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(); + //RiffList::m_size includes fourCC field which we have already read + next_list += (hdrl_list.m_size - 4); + //parseHdrlList sets m_is_indx_present flag which would be used later + if(parseHdrlList(in_str)) + { + in_str.seekg(next_list); + + RiffList some_list; + in_str >> some_list; + + //an optional section INFO + 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(); + //RiffList::m_size includes fourCC field which we have already read + next_list += (some_list.m_size - 4); + parseInfo(in_str); + + in_str.seekg(next_list); + in_str >> some_list; + } + + //an optional section JUNK + skipJunk(some_list, in_str); + + //we are expecting to find here movi list. Must present in avi + 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 is set to true we should find index + if(m_is_indx_present) + { + //we are expecting to find index section after movi list + 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); + //we are not going anywhere else + } + else + { + printError(in_str, index_chunk, IDX1_CC); + } + } + //index not present or we were not able to find it + //parsing movi list + if(!is_index_found) + { + //not implemented + parseMovi(in_str, in_frame_list); + + fprintf(stderr, "Failed to parse avi: index was not found\n"); + //we are not going anywhere else + } + } + 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; } // Return the type of the capture object: CAP_VFW, etc... + MotionJpegCapture(const String&); + + bool open(const String&); + void close(); +protected: + + bool parseRiff(MjpegInputStream& in_str); + + inline uint64_t getFramePos() const; + std::vector 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; + + //frame width/height and fps could be different for + //each frame/stream. At the moment we suppose that they + //stays the same within single avi file. + 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 MotionJpegCapture::readFrame(frame_iterator it) +{ + m_file_stream.seekg(it->first); + + RiffChunk chunk; + m_file_stream >> chunk; + + std::vector 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 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(); + //RiffList::m_size includes fourCC field which we have already read + 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 createMotionJpegCapture(const String& filename) { - return Ptr(); + Ptr mjdecoder(new MotionJpegCapture(filename)); + if( mjdecoder->isOpened() ) + return mjdecoder; + return Ptr(); } } -- GitLab