set(videoio_hdrs
${CMAKE_CURRENT_LIST_DIR}/src/precomp.hpp
)
-
set(videoio_srcs
${CMAKE_CURRENT_LIST_DIR}/src/cap.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_images.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_encoder.cpp
${CMAKE_CURRENT_LIST_DIR}/src/cap_mjpeg_decoder.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/src/container_avi.cpp
)
file(GLOB videoio_ext_hdrs
--- /dev/null
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef CONTAINER_AVI_HPP
+#define CONTAINER_AVI_HPP
+
+#ifndef __OPENCV_BUILD
+# error this is a private header which should not be used from outside of the OpenCV library
+#endif
+
+#include "opencv2/core/cvdef.h"
+#include "opencv2/videoio/videoio_c.h"
+#include <deque>
+
+namespace cv
+{
+
+/*
+AVI struct:
+
+RIFF ('AVI '
+ LIST ('hdrl'
+ 'avih'(<Main AVI Header>)
+ LIST ('strl'
+ 'strh'(<Stream header>)
+ 'strf'(<Stream format>)
+ [ 'strd'(<Additional header data>) ]
+ [ 'strn'(<Stream name>) ]
+ [ 'indx'(<Odml index data>) ]
+ ...
+ )
+ [LIST ('strl' ...)]
+ [LIST ('strl' ...)]
+ ...
+ [LIST ('odml'
+ 'dmlh'(<ODML header data>)
+ ...
+ )
+ ]
+ ...
+ )
+ [LIST ('INFO' ...)]
+ [JUNK]
+ LIST ('movi'
+ {{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
+ {xxdb|xxdc|xxpc|xxwb}(<Data>)
+ {xxdb|xxdc|xxpc|xxwb}(<Data>)
+ ...
+ )
+ ...
+ }
+ ...
+ )
+ ['idx1' (<AVI Index>) ]
+ )
+
+ {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<uint64_t, uint32_t> > frame_list;
+typedef frame_list::iterator frame_iterator;
+struct RiffChunk;
+struct RiffList;
+class VideoInputStream;
+enum Codecs { MJPEG };
+
+//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 CV_EXPORTS AVIReadContainer
+{
+public:
+ AVIReadContainer();
+
+ void initStream(const String& filename);
+ void initStream(Ptr<VideoInputStream> m_file_stream_);
+
+ void close();
+ //stores founded frames in m_frame_list which can be accessed via getFrames
+ bool parseAvi(Codecs codec_) { return parseAviWithFrameList(m_frame_list, codec_); }
+ //stores founded frames in in_frame_list. getFrames() would return empty list
+ bool parseAvi(frame_list& in_frame_list, Codecs codec_) { return parseAviWithFrameList(in_frame_list, codec_); }
+ size_t getFramesCount() { return m_frame_list.size(); }
+ frame_list& getFrames() { return m_frame_list; }
+ unsigned int getWidth() { return m_width; }
+ unsigned int getHeight() { return m_height; }
+ double getFps() { return m_fps; }
+ std::vector<char> readFrame(frame_iterator it);
+ bool parseRiff(frame_list &m_mjpeg_frames);
+
+protected:
+
+ bool parseAviWithFrameList(frame_list& in_frame_list, Codecs codec_);
+ void skipJunk(RiffChunk& chunk);
+ void skipJunk(RiffList& list);
+ bool parseHdrlList(Codecs codec_);
+ bool parseIndex(unsigned int index_size, frame_list& in_frame_list);
+ bool parseMovi(frame_list& in_frame_list)
+ {
+ //not implemented
+ in_frame_list.empty();
+ return true;
+ }
+ bool parseStrl(char stream_id, Codecs codec_);
+ bool parseInfo()
+ {
+ //not implemented
+ return true;
+ }
+
+ void printError(RiffList& list, unsigned int expected_fourcc);
+
+ void printError(RiffChunk& chunk, unsigned int expected_fourcc);
+
+ Ptr<VideoInputStream> m_file_stream;
+ unsigned int m_stream_id;
+ unsigned long long int m_movi_start;
+ unsigned long long int m_movi_end;
+ frame_list m_frame_list;
+ unsigned int m_width;
+ unsigned int m_height;
+ double m_fps;
+ bool m_is_indx_present;
+};
+
+enum { COLORSPACE_GRAY=0, COLORSPACE_RGBA=1, COLORSPACE_BGR=2, COLORSPACE_YUV444P=3 };
+enum StreamType { db, dc, pc, wb };
+class BitStream;
+
+// {xxdb|xxdc|xxpc|xxwb}
+// xx - stream number: 00, 01, 02, ...
+// db - uncompressed video frame
+// dc - commpressed video frame
+// pc - palette change
+// wb - audio frame
+
+
+class CV_EXPORTS AVIWriteContainer
+{
+public:
+ AVIWriteContainer();
+ ~AVIWriteContainer();
+
+ bool initContainer(const String& filename, double fps, Size size, bool iscolor);
+ void startWriteAVI(int stream_count);
+ void writeStreamHeader(Codecs codec_);
+ void startWriteChunk(int fourcc);
+ void endWriteChunk();
+
+ int getAVIIndex(int stream_number, StreamType strm_type);
+ void writeIndex(int stream_number, StreamType strm_type);
+ void finishWriteAVI();
+
+ bool isOpenedStream() const;
+ bool isEmptyFrameOffset() const { return frameOffset.empty(); }
+ int getWidth() const { return width; }
+ int getHeight() const { return height; }
+ int getChannels() const { return channels; }
+ size_t getMoviPointer() const { return moviPointer; }
+ size_t getStreamPos() const;
+
+ void pushFrameOffset(size_t elem) { frameOffset.push_back(elem); }
+ void pushFrameSize(size_t elem) { frameSize.push_back(elem); }
+ bool isEmptyFrameSize() const { return frameSize.empty(); }
+ size_t atFrameSize(size_t i) const { return frameSize[i]; }
+ size_t countFrameSize() const { return frameSize.size(); }
+ void jputStreamShort(int val);
+ void putStreamBytes(const uchar* buf, int count);
+ void putStreamByte(int val);
+ void jputStream(unsigned currval);
+ void jflushStream(unsigned currval, int bitIdx);
+
+private:
+ Ptr<BitStream> strm;
+ int outfps;
+ int width, height, channels;
+ size_t moviPointer;
+ std::vector<size_t> frameOffset, frameSize, AVIChunkSizeIndex, frameNumIndexes;
+};
+
+}
+
+#endif //CONTAINER_AVI_HPP
//M*/
#include "precomp.hpp"
-#include <deque>
-#include <stdint.h>
+#include "opencv2/videoio/container_avi.private.hpp"
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; // 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; // height 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'(<Main AVI Header>)
- LIST ('strl'
- 'strh'(<Stream header>)
- 'strf'(<Stream format>)
- [ 'strd'(<Additional header data>) ]
- [ 'strn'(<Stream name>) ]
- [ 'indx'(<Odml index data>) ]
- ...
- )
- [LIST ('strl' ...)]
- [LIST ('strl' ...)]
- ...
- [LIST ('odml'
- 'dmlh'(<ODML header data>)
- ...
- )
- ]
- ...
- )
- [LIST ('INFO' ...)]
- [JUNK]
- LIST ('movi'
- {{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
- {xxdb|xxdc|xxpc|xxwb}(<Data>)
- {xxdb|xxdc|xxpc|xxwb}(<Data>)
- ...
- )
- ...
- }
- ...
- )
- ['idx1' (<AVI Index>) ]
- )
-
- {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<uint64_t, uint32_t> > 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 can 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_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&)
-{
- //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;
- CV_Assert(number_of_streams < 0xFF);
- m_width = avi_hdr.dwWidth;
- m_height = avi_hdr.dwHeight;
-
- //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:
void close();
protected:
- bool parseRiff(MjpegInputStream& in_str);
-
inline uint64_t getFramePos() const;
- std::vector<char> readFrame(frame_iterator it);
- MjpegInputStream m_file_stream;
+ Ptr<AVIReadContainer> m_avi_container;
bool m_is_first_frame;
frame_list m_mjpeg_frames;
}
}
-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[0]), chunk.m_size); // result.data() failed with MSVS2008
-
- return result;
-}
-
bool MotionJpegCapture::grabFrame()
{
if(isOpened())
{
if(m_frame_iterator != m_mjpeg_frames.end())
{
- std::vector<char> data = readFrame(m_frame_iterator);
+ std::vector<char> data = m_avi_container->readFrame(m_frame_iterator);
if(data.size())
{
MotionJpegCapture::MotionJpegCapture(const String& filename)
{
+ m_avi_container = makePtr<AVIReadContainer>();
+ m_avi_container->initStream(filename);
open(filename);
}
void MotionJpegCapture::close()
{
- m_file_stream.close();
+ m_avi_container->close();
m_frame_iterator = m_mjpeg_frames.end();
}
{
close();
- m_file_stream.open(filename);
+ m_avi_container = makePtr<AVIReadContainer>();
+ m_avi_container->initStream(filename);
m_frame_iterator = m_mjpeg_frames.end();
m_is_first_frame = true;
- if(!parseRiff(m_file_stream))
+ if(!m_avi_container->parseRiff(m_mjpeg_frames))
{
close();
- }
-
- return isOpened();
-}
-
-
-bool MotionJpegCapture::parseRiff(MjpegInputStream& in_str)
-{
- bool result = false;
- while(in_str)
+ } else
{
- 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;
- }
+ m_frame_width = m_avi_container->getWidth();
+ m_frame_height = m_avi_container->getHeight();
+ m_fps = m_avi_container->getFps();
}
- return result;
+ return isOpened();
}
Ptr<IVideoCapture> createMotionJpegCapture(const String& filename)
//M*/
#include "precomp.hpp"
+#include "opencv2/videoio/container_avi.private.hpp"
+
#include <vector>
#include <deque>
+#include <iostream>
+#include <cstdlib>
#if CV_NEON
#define WITH_NEON
namespace cv
{
-namespace mjpeg
-{
-
-enum { COLORSPACE_GRAY=0, COLORSPACE_RGBA=1, COLORSPACE_BGR=2, COLORSPACE_YUV444P=3 };
-
-#define fourCC(a,b,c,d) ((int)((uchar(d)<<24) | (uchar(c)<<16) | (uchar(b)<<8) | uchar(a)))
-
-static const int AVIH_STRH_SIZE = 56;
-static const int STRF_SIZE = 40;
-static const int AVI_DWFLAG = 0x00000910;
-static const int AVI_DWSCALE = 1;
-static const int AVI_DWQUALITY = -1;
-static const int JUNK_SEEK = 4096;
-static const int AVIIF_KEYFRAME = 0x10;
-static const int MAX_BYTES_PER_SEC = 99999999;
-static const int SUG_BUFFER_SIZE = 1048576;
static const unsigned bit_mask[] =
{
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
};
-class BitStream
-{
-public:
- enum
- {
- DEFAULT_BLOCK_SIZE = (1 << 15),
- huff_val_shift = 20,
- huff_code_mask = (1 << huff_val_shift) - 1
- };
-
- BitStream()
- {
- m_buf.resize(DEFAULT_BLOCK_SIZE + 1024);
- m_start = &m_buf[0];
- m_end = m_start + DEFAULT_BLOCK_SIZE;
- m_is_opened = false;
- m_f = 0;
- m_current = 0;
- m_pos = 0;
- }
+static const uchar huff_val_shift = 20;
+static const int huff_code_mask = (1 << huff_val_shift) - 1;
- ~BitStream()
- {
- close();
- }
-
- bool open(const String& filename)
- {
- close();
- m_f = fopen(filename.c_str(), "wb");
- if( !m_f )
- return false;
- m_current = m_start;
- m_pos = 0;
- return true;
- }
-
- bool isOpened() const { return m_f != 0; }
+static bool createEncodeHuffmanTable( const int* src, unsigned* table, int max_size )
+{
+ int i, k;
+ int min_val = INT_MAX, max_val = INT_MIN;
+ int size;
- void close()
+ /* calc min and max values in the table */
+ for( i = 1, k = 1; src[k] >= 0; i++ )
{
- writeBlock();
- if( m_f )
- fclose(m_f);
- m_f = 0;
- }
+ int code_count = src[k++];
- void writeBlock()
- {
- size_t wsz0 = m_current - m_start;
- if( wsz0 > 0 && m_f )
+ for( code_count += k; k < code_count; k++ )
{
- size_t wsz = fwrite(m_start, 1, wsz0, m_f);
- CV_Assert( wsz == wsz0 );
+ int val = src[k] >> huff_val_shift;
+ if( val < min_val )
+ min_val = val;
+ if( val > max_val )
+ max_val = val;
}
- m_pos += wsz0;
- m_current = m_start;
}
- size_t getPos() const
- {
- return (size_t)(m_current - m_start) + m_pos;
- }
+ size = max_val - min_val + 3;
- void putByte(int val)
+ if( size > max_size )
{
- *m_current++ = (uchar)val;
- if( m_current >= m_end )
- writeBlock();
- }
-
- void putBytes(const uchar* buf, int count)
- {
- uchar* data = (uchar*)buf;
- CV_Assert(m_f && data && m_current && count >= 0);
- if( m_current >= m_end )
- writeBlock();
-
- while( count )
- {
- int l = (int)(m_end - m_current);
-
- if (l > count)
- l = count;
-
- if( l > 0 )
- {
- memcpy(m_current, data, l);
- m_current += l;
- data += l;
- count -= l;
- }
- if( m_current >= m_end )
- writeBlock();
- }
+ CV_Error(CV_StsOutOfRange, "too big maximum Huffman code size");
+ return false;
}
- void putShort(int val)
- {
- m_current[0] = (uchar)val;
- m_current[1] = (uchar)(val >> 8);
- m_current += 2;
- if( m_current >= m_end )
- writeBlock();
- }
+ memset( table, 0, size*sizeof(table[0]));
- void putInt(int val)
- {
- m_current[0] = (uchar)val;
- m_current[1] = (uchar)(val >> 8);
- m_current[2] = (uchar)(val >> 16);
- m_current[3] = (uchar)(val >> 24);
- m_current += 4;
- if( m_current >= m_end )
- writeBlock();
- }
+ table[0] = min_val;
+ table[1] = size - 2;
- void jputShort(int val)
+ for( i = 1, k = 1; src[k] >= 0; i++ )
{
- m_current[0] = (uchar)(val >> 8);
- m_current[1] = (uchar)val;
- m_current += 2;
- if( m_current >= m_end )
- writeBlock();
- }
+ int code_count = src[k++];
- void patchInt(int val, size_t pos)
- {
- if( pos >= m_pos )
- {
- ptrdiff_t delta = pos - m_pos;
- CV_Assert( delta < m_current - m_start );
- m_start[delta] = (uchar)val;
- m_start[delta+1] = (uchar)(val >> 8);
- m_start[delta+2] = (uchar)(val >> 16);
- m_start[delta+3] = (uchar)(val >> 24);
- }
- else
+ for( code_count += k; k < code_count; k++ )
{
- long fpos = ftell(m_f);
- fseek(m_f, (long)pos, SEEK_SET);
- uchar buf[] = { (uchar)val, (uchar)(val >> 8), (uchar)(val >> 16), (uchar)(val >> 24) };
- fwrite(buf, 1, 4, m_f);
- fseek(m_f, fpos, SEEK_SET);
- }
- }
+ int val = src[k] >> huff_val_shift;
+ int code = src[k] & huff_code_mask;
- void jput(unsigned currval)
- {
- uchar v;
- uchar* ptr = m_current;
- v = (uchar)(currval >> 24);
- *ptr++ = v;
- if( v == 255 )
- *ptr++ = 0;
- v = (uchar)(currval >> 16);
- *ptr++ = v;
- if( v == 255 )
- *ptr++ = 0;
- v = (uchar)(currval >> 8);
- *ptr++ = v;
- if( v == 255 )
- *ptr++ = 0;
- v = (uchar)currval;
- *ptr++ = v;
- if( v == 255 )
- *ptr++ = 0;
- m_current = ptr;
- if( m_current >= m_end )
- writeBlock();
- }
-
- void jflush(unsigned currval, int bitIdx)
- {
- uchar v;
- uchar* ptr = m_current;
- currval |= (1 << bitIdx)-1;
- while( bitIdx < 32 )
- {
- v = (uchar)(currval >> 24);
- *ptr++ = v;
- if( v == 255 )
- *ptr++ = 0;
- currval <<= 8;
- bitIdx += 8;
+ table[val - min_val + 2] = (code << 8) | i;
}
- m_current = ptr;
- if( m_current >= m_end )
- writeBlock();
- }
-
- static bool createEncodeHuffmanTable( const int* src, unsigned* table, int max_size )
- {
- int i, k;
- int min_val = INT_MAX, max_val = INT_MIN;
- int size;
-
- /* calc min and max values in the table */
- for( i = 1, k = 1; src[k] >= 0; i++ )
- {
- int code_count = src[k++];
-
- for( code_count += k; k < code_count; k++ )
- {
- int val = src[k] >> huff_val_shift;
- if( val < min_val )
- min_val = val;
- if( val > max_val )
- max_val = val;
- }
- }
-
- size = max_val - min_val + 3;
-
- if( size > max_size )
- {
- CV_Error(CV_StsOutOfRange, "too big maximum Huffman code size");
- return false;
- }
-
- memset( table, 0, size*sizeof(table[0]));
-
- table[0] = min_val;
- table[1] = size - 2;
-
- for( i = 1, k = 1; src[k] >= 0; i++ )
- {
- int code_count = src[k++];
-
- for( code_count += k; k < code_count; k++ )
- {
- int val = src[k] >> huff_val_shift;
- int code = src[k] & huff_code_mask;
-
- table[val - min_val + 2] = (code << 8) | i;
- }
- }
- return true;
}
+ return true;
+}
- static int* createSourceHuffmanTable(const uchar* src, int* dst,
+static int* createSourceHuffmanTable(const uchar* src, int* dst,
int max_bits, int first_bits)
+{
+ int i, val_idx, code = 0;
+ int* table = dst;
+ *dst++ = first_bits;
+ for (i = 1, val_idx = max_bits; i <= max_bits; i++)
{
- int i, val_idx, code = 0;
- int* table = dst;
- *dst++ = first_bits;
- for (i = 1, val_idx = max_bits; i <= max_bits; i++)
+ int code_count = src[i - 1];
+ dst[0] = code_count;
+ code <<= 1;
+ for (int k = 0; k < code_count; k++)
{
- int code_count = src[i - 1];
- dst[0] = code_count;
- code <<= 1;
- for (int k = 0; k < code_count; k++)
- {
- dst[k + 1] = (src[val_idx + k] << huff_val_shift) | (code + k);
- }
- code += code_count;
- dst += code_count + 1;
- val_idx += code_count;
+ dst[k + 1] = (src[val_idx + k] << huff_val_shift) | (code + k);
}
- dst[0] = -1;
- return table;
+ code += code_count;
+ dst += code_count + 1;
+ val_idx += code_count;
}
+ dst[0] = -1;
+ return table;
+}
-protected:
- std::vector<uchar> m_buf;
- uchar* m_start;
- uchar* m_end;
- uchar* m_current;
- size_t m_pos;
- bool m_is_opened;
- FILE* m_f;
-};
+namespace mjpeg
+{
class mjpeg_buffer
{
{
rawstream = false;
nstripes = -1;
- height = 0;
- width = 0;
- moviPointer = 0;
- channels = 0;
- outfps = 0;
quality = 0;
}
void close()
{
- if( !strm.isOpened() )
+ if( !container.isOpenedStream() )
return;
- if( !frameOffset.empty() && !rawstream )
+ if( !container.isEmptyFrameOffset() && !rawstream )
{
- endWriteChunk(); // end LIST 'movi'
- writeIndex();
- finishWriteAVI();
+ container.endWriteChunk(); // end LIST 'movi'
+ container.writeIndex(0, dc);
+ container.finishWriteAVI();
}
- strm.close();
- frameOffset.clear();
- frameSize.clear();
- AVIChunkSizeIndex.clear();
- frameNumIndexes.clear();
}
bool open(const String& filename, double fps, Size size, bool iscolor)
if( strcmp(ext, ".avi") != 0 && strcmp(ext, ".AVI") != 0 && strcmp(ext, ".Avi") != 0 )
return false;
- bool ok = strm.open(filename);
- if( !ok )
+ if( !container.initContainer(filename, fps, size, iscolor) )
return false;
CV_Assert(fps >= 1);
- outfps = cvRound(fps);
- width = size.width;
- height = size.height;
quality = 75;
rawstream = false;
- channels = iscolor ? 3 : 1;
if( !rawstream )
{
- startWriteAVI();
- writeStreamHeader();
+ container.startWriteAVI(1); // count stream
+ container.writeStreamHeader(MJPEG);
}
//printf("motion jpeg stream %s has been successfully opened\n", filename.c_str());
return true;
}
- bool isOpened() const { return strm.isOpened(); }
-
- void startWriteAVI()
- {
- startWriteChunk(fourCC('R', 'I', 'F', 'F'));
-
- strm.putInt(fourCC('A', 'V', 'I', ' '));
-
- startWriteChunk(fourCC('L', 'I', 'S', 'T'));
-
- strm.putInt(fourCC('h', 'd', 'r', 'l'));
- strm.putInt(fourCC('a', 'v', 'i', 'h'));
- strm.putInt(AVIH_STRH_SIZE);
- strm.putInt(cvRound(1e6 / outfps));
- strm.putInt(MAX_BYTES_PER_SEC);
- strm.putInt(0);
- strm.putInt(AVI_DWFLAG);
-
- frameNumIndexes.push_back(strm.getPos());
-
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(1); // number of streams
- strm.putInt(SUG_BUFFER_SIZE);
- strm.putInt(width);
- strm.putInt(height);
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(0);
- }
-
- void writeStreamHeader()
- {
- // strh
- startWriteChunk(fourCC('L', 'I', 'S', 'T'));
-
- strm.putInt(fourCC('s', 't', 'r', 'l'));
- strm.putInt(fourCC('s', 't', 'r', 'h'));
- strm.putInt(AVIH_STRH_SIZE);
- strm.putInt(fourCC('v', 'i', 'd', 's'));
- strm.putInt(fourCC('M', 'J', 'P', 'G'));
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(AVI_DWSCALE);
- strm.putInt(outfps);
- strm.putInt(0);
-
- frameNumIndexes.push_back(strm.getPos());
-
- strm.putInt(0);
- strm.putInt(SUG_BUFFER_SIZE);
- strm.putInt(AVI_DWQUALITY);
- strm.putInt(0);
- strm.putShort(0);
- strm.putShort(0);
- strm.putShort(width);
- strm.putShort(height);
-
- // strf (use the BITMAPINFOHEADER for video)
- startWriteChunk(fourCC('s', 't', 'r', 'f'));
-
- strm.putInt(STRF_SIZE);
- strm.putInt(width);
- strm.putInt(height);
- strm.putShort(1); // planes (1 means interleaved data (after decompression))
-
- strm.putShort(8 * channels); // bits per pixel
- strm.putInt(fourCC('M', 'J', 'P', 'G'));
- strm.putInt(width * height * channels);
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(0);
- strm.putInt(0);
- // Must be indx chunk
- endWriteChunk(); // end strf
- endWriteChunk(); // end strl
-
- // odml
- startWriteChunk(fourCC('L', 'I', 'S', 'T'));
- strm.putInt(fourCC('o', 'd', 'm', 'l'));
- startWriteChunk(fourCC('d', 'm', 'l', 'h'));
-
- frameNumIndexes.push_back(strm.getPos());
-
- strm.putInt(0);
- strm.putInt(0);
-
- endWriteChunk(); // end dmlh
- endWriteChunk(); // end odml
-
- endWriteChunk(); // end hdrl
-
- // JUNK
- startWriteChunk(fourCC('J', 'U', 'N', 'K'));
- size_t pos = strm.getPos();
- for( ; pos < (size_t)JUNK_SEEK; pos += 4 )
- strm.putInt(0);
- endWriteChunk(); // end JUNK
- // movi
- startWriteChunk(fourCC('L', 'I', 'S', 'T'));
- moviPointer = strm.getPos();
- strm.putInt(fourCC('m', 'o', 'v', 'i'));
- }
-
- void startWriteChunk(int fourcc)
- {
- CV_Assert(fourcc != 0);
- strm.putInt(fourcc);
-
- AVIChunkSizeIndex.push_back(strm.getPos());
- strm.putInt(0);
- }
-
- void endWriteChunk()
- {
- if( !AVIChunkSizeIndex.empty() )
- {
- size_t currpos = strm.getPos();
- size_t pospos = AVIChunkSizeIndex.back();
- AVIChunkSizeIndex.pop_back();
- int chunksz = (int)(currpos - (pospos + 4));
- strm.patchInt(chunksz, pospos);
- }
- }
-
- void writeIndex()
- {
- // old style AVI index. Must be Open-DML index
- startWriteChunk(fourCC('i', 'd', 'x', '1'));
- int nframes = (int)frameOffset.size();
- for( int i = 0; i < nframes; i++ )
- {
- strm.putInt(fourCC('0', '0', 'd', 'c'));
- strm.putInt(AVIIF_KEYFRAME);
- strm.putInt((int)frameOffset[i]);
- strm.putInt((int)frameSize[i]);
- }
- endWriteChunk(); // End idx1
- }
-
- void finishWriteAVI()
- {
- int nframes = (int)frameOffset.size();
- // Record frames numbers to AVI Header
- while (!frameNumIndexes.empty())
- {
- size_t ppos = frameNumIndexes.back();
- frameNumIndexes.pop_back();
- strm.patchInt(nframes, ppos);
- }
- endWriteChunk(); // end RIFF
- }
+ bool isOpened() const { return container.isOpenedStream(); }
void write(InputArray _img)
{
Mat img = _img.getMat();
- size_t chunkPointer = strm.getPos();
+ size_t chunkPointer = container.getStreamPos();
int input_channels = img.channels();
int colorspace = -1;
+ int imgWidth = img.cols;
+ int frameWidth = container.getWidth();
+ int imgHeight = img.rows;
+ int frameHeight = container.getHeight();
+ int channels = container.getChannels();
+
if( input_channels == 1 && channels == 1 )
{
- CV_Assert( img.cols == width && img.rows == height );
+ CV_Assert( imgWidth == frameWidth && imgHeight == frameHeight );
colorspace = COLORSPACE_GRAY;
}
else if( input_channels == 4 )
{
- CV_Assert( img.cols == width && img.rows == height && channels == 3 );
+ CV_Assert( imgWidth == frameWidth && imgHeight == frameHeight && channels == 3 );
colorspace = COLORSPACE_RGBA;
}
else if( input_channels == 3 )
{
- CV_Assert( img.cols == width && img.rows == height && channels == 3 );
+ CV_Assert( imgWidth == frameWidth && imgHeight == frameHeight && channels == 3 );
colorspace = COLORSPACE_BGR;
}
else if( input_channels == 1 && channels == 3 )
{
- CV_Assert( img.cols == width && img.rows == height*3 );
+ CV_Assert( imgWidth == frameWidth && imgHeight == frameHeight*3 );
colorspace = COLORSPACE_YUV444P;
}
else
CV_Error(CV_StsBadArg, "Invalid combination of specified video colorspace and the input image colorspace");
- if( !rawstream )
- startWriteChunk(fourCC('0', '0', 'd', 'c'));
+ if( !rawstream ) {
+ int avi_index = container.getAVIIndex(0, dc);
+ container.startWriteChunk(avi_index);
+ }
writeFrameData(img.data, (int)img.step, colorspace, input_channels);
if( !rawstream )
{
- frameOffset.push_back(chunkPointer - moviPointer);
- frameSize.push_back(strm.getPos() - chunkPointer - 8); // Size excludes '00dc' and size field
- endWriteChunk(); // end '00dc'
+ size_t tempChunkPointer = container.getStreamPos();
+ size_t moviPointer = container.getMoviPointer();
+ container.pushFrameOffset(chunkPointer - moviPointer);
+ container.pushFrameSize(tempChunkPointer - chunkPointer - 8); // Size excludes '00dc' and size field
+ container.endWriteChunk(); // end '00dc'
}
}
if( propId == VIDEOWRITER_PROP_QUALITY )
return quality;
if( propId == VIDEOWRITER_PROP_FRAMEBYTES )
- return frameSize.empty() ? 0. : (double)frameSize.back();
+ {
+ bool isEmpty = container.isEmptyFrameSize();
+ return isEmpty ? 0. : container.atFrameSize(container.countFrameSize() - 1);
+ }
if( propId == VIDEOWRITER_PROP_NSTRIPES )
return nstripes;
return 0.;
void writeFrameData( const uchar* data, int step, int colorspace, int input_channels );
protected:
- int outfps;
- int width, height, channels;
double quality;
- size_t moviPointer;
- std::vector<size_t> frameOffset, frameSize, AVIChunkSizeIndex, frameNumIndexes;
bool rawstream;
mjpeg_buffer_keeper buffers_list;
double nstripes;
- BitStream strm;
+ AVIWriteContainer container;
};
#define DCT_DESCALE(x, n) (((x) + (((int)1) << ((n) - 1))) >> (n))
}
//double total_dct = 0, total_cvt = 0;
+ int width = container.getWidth();
+ int height = container.getHeight();
+ int channels = container.getChannels();
+
CV_Assert( data && width > 0 && height > 0 );
// encode the header and tables
double inv_quality = 1./_quality;
// Encode header
- strm.putBytes( (const uchar*)jpegHeader, sizeof(jpegHeader) - 1 );
+ container.putStreamBytes( (const uchar*)jpegHeader, sizeof(jpegHeader) - 1 );
// Encode quantization tables
for( i = 0; i < (channels > 1 ? 2 : 1); i++ )
const uchar* qtable = i == 0 ? jpegTableK1_T : jpegTableK2_T;
int chroma_scale = i > 0 ? luma_count : 1;
- strm.jputShort( 0xffdb ); // DQT marker
- strm.jputShort( 2 + 65*1 ); // put single qtable
- strm.putByte( 0*16 + i ); // 8-bit table
+ container.jputStreamShort( 0xffdb ); // DQT marker
+ container.jputStreamShort( 2 + 65*1 ); // put single qtable
+ container.putStreamByte( 0*16 + i ); // 8-bit table
// put coefficients
for( j = 0; j < 64; j++ )
qval = 255;
fdct_qtab[i][idx] = (short)(cvRound((1 << (postshift + 11)))/
(qval*chroma_scale*idct_prescale[idx]));
- strm.putByte( qval );
+ container.putStreamByte( qval );
}
}
int idx = i >= 2;
int tableSize = 16 + (is_ac_tab ? 162 : 12);
- strm.jputShort( 0xFFC4 ); // DHT marker
- strm.jputShort( 3 + tableSize ); // define one huffman table
- strm.putByte( is_ac_tab*16 + idx ); // put DC/AC flag and table index
- strm.putBytes( htable, tableSize ); // put table
+ container.jputStreamShort( 0xFFC4 ); // DHT marker
+ container.jputStreamShort( 3 + tableSize ); // define one huffman table
+ container.putStreamByte( is_ac_tab*16 + idx ); // put DC/AC flag and table index
+ container.putStreamBytes( htable, tableSize ); // put table
- BitStream::createEncodeHuffmanTable( BitStream::createSourceHuffmanTable(
- htable, hbuffer, 16, 9 ), is_ac_tab ? huff_ac_tab[idx] :
- huff_dc_tab[idx], is_ac_tab ? 256 : 16 );
+ createEncodeHuffmanTable(createSourceHuffmanTable( htable, hbuffer, 16, 9 ),
+ is_ac_tab ? huff_ac_tab[idx] : huff_dc_tab[idx],
+ is_ac_tab ? 256 : 16 );
}
// put frame header
- strm.jputShort( 0xFFC0 ); // SOF0 marker
- strm.jputShort( 8 + 3*channels ); // length of frame header
- strm.putByte( 8 ); // sample precision
- strm.jputShort( height );
- strm.jputShort( width );
- strm.putByte( channels ); // number of components
+ container.jputStreamShort( 0xFFC0 ); // SOF0 marker
+ container.jputStreamShort( 8 + 3*channels ); // length of frame header
+ container.putStreamByte( 8 ); // sample precision
+ container.jputStreamShort( height );
+ container.jputStreamShort( width );
+ container.putStreamByte( channels ); // number of components
for( i = 0; i < channels; i++ )
{
- strm.putByte( i + 1 ); // (i+1)-th component id (Y,U or V)
+ container.putStreamByte( i + 1 ); // (i+1)-th component id (Y,U or V)
if( i == 0 )
- strm.putByte(x_scale*16 + y_scale); // chroma scale factors
+ container.putStreamByte(x_scale*16 + y_scale); // chroma scale factors
else
- strm.putByte(1*16 + 1);
- strm.putByte( i > 0 ); // quantization table idx
+ container.putStreamByte(1*16 + 1);
+ container.putStreamByte( i > 0 ); // quantization table idx
}
// put scan header
- strm.jputShort( 0xFFDA ); // SOS marker
- strm.jputShort( 6 + 2*channels ); // length of scan header
- strm.putByte( channels ); // number of components in the scan
+ container.jputStreamShort( 0xFFDA ); // SOS marker
+ container.jputStreamShort( 6 + 2*channels ); // length of scan header
+ container.putStreamByte( channels ); // number of components in the scan
for( i = 0; i < channels; i++ )
{
- strm.putByte( i+1 ); // component id
- strm.putByte( (i>0)*16 + (i>0) );// selection of DC & AC tables
+ container.putStreamByte( i+1 ); // component id
+ container.putStreamByte( (i>0)*16 + (i>0) );// selection of DC & AC tables
}
- strm.jputShort(0*256 + 63); // start and end of spectral selection - for
+ container.jputStreamShort(0*256 + 63); // start and end of spectral selection - for
// sequential DCT start is 0 and end is 63
- strm.putByte( 0 ); // successive approximation bit position
+ container.putStreamByte( 0 ); // successive approximation bit position
// high & low - (0,0) for sequential DCT
buffers_list.reset();
for(unsigned k = 0; k < last_data_elem; ++k)
{
- strm.jput(v[k]);
+ container.jputStream(v[k]);
}
- strm.jflush(v[last_data_elem], 32 - buffers_list.get_last_bit_len());
- strm.jputShort( 0xFFD9 ); // EOI marker
+ container.jflushStream(v[last_data_elem], 32 - buffers_list.get_last_bit_len());
+ container.jputStreamShort( 0xFFD9 ); // EOI marker
/*printf("total dct = %.1fms, total cvt = %.1fms\n",
total_dct*1000./cv::getTickFrequency(),
total_cvt*1000./cv::getTickFrequency());*/
- size_t pos = strm.getPos();
+ size_t pos = container.getStreamPos();
size_t pos1 = (pos + 3) & ~3;
for( ; pos < pos1; pos++ )
- strm.putByte(0);
+ container.putStreamByte(0);
}
}
--- /dev/null
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#include "opencv2/videoio/container_avi.private.hpp"
+
+namespace cv
+{
+
+const unsigned int RIFF_CC = CV_FOURCC('R','I','F','F');
+const unsigned int LIST_CC = CV_FOURCC('L','I','S','T');
+const unsigned int HDRL_CC = CV_FOURCC('h','d','r','l');
+const unsigned int AVIH_CC = CV_FOURCC('a','v','i','h');
+const unsigned int STRL_CC = CV_FOURCC('s','t','r','l');
+const unsigned int STRH_CC = CV_FOURCC('s','t','r','h');
+const unsigned int STRF_CC = CV_FOURCC('s','t','r','f');
+const unsigned int VIDS_CC = CV_FOURCC('v','i','d','s');
+const unsigned int MJPG_CC = CV_FOURCC('M','J','P','G');
+const unsigned int MOVI_CC = CV_FOURCC('m','o','v','i');
+const unsigned int IDX1_CC = CV_FOURCC('i','d','x','1');
+const unsigned int AVI_CC = CV_FOURCC('A','V','I',' ');
+const unsigned int AVIX_CC = CV_FOURCC('A','V','I','X');
+const unsigned int JUNK_CC = CV_FOURCC('J','U','N','K');
+const unsigned int INFO_CC = CV_FOURCC('I','N','F','O');
+const unsigned int ODML_CC = CV_FOURCC('o','d','m','l');
+const unsigned int DMLH_CC = CV_FOURCC('d','m','l','h');
+
+String fourccToString(unsigned int fourcc);
+
+#ifndef DWORD
+typedef unsigned int DWORD;
+#endif
+#ifndef WORD
+typedef unsigned short int WORD;
+#endif
+#ifndef LONG
+typedef int 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
+{
+ unsigned int fccType; // 'vids', 'auds', 'txts'...
+ unsigned int 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; // height 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
+{
+ unsigned int m_four_cc;
+ unsigned int m_size;
+};
+
+struct RiffList
+{
+ unsigned int m_riff_or_list_cc;
+ unsigned int m_size;
+ unsigned int m_list_type_cc;
+};
+
+class VideoInputStream
+{
+public:
+ VideoInputStream();
+ VideoInputStream(const String& filename);
+ ~VideoInputStream();
+ VideoInputStream& read(char*, unsigned long long int);
+ VideoInputStream& seekg(unsigned long long int);
+ unsigned long long int tellg();
+ bool isOpened() const;
+ bool open(const String& filename);
+ void close();
+ operator bool();
+ VideoInputStream& operator=(const VideoInputStream& stream);
+
+private:
+ bool m_is_valid;
+ String m_fname;
+ FILE* m_f;
+};
+
+#pragma pack(pop)
+
+inline VideoInputStream& operator >> (VideoInputStream& is, AviMainHeader& avih)
+{
+ is.read((char*)(&avih), sizeof(AviMainHeader));
+ return is;
+}
+inline VideoInputStream& operator >> (VideoInputStream& is, AviStreamHeader& strh)
+{
+ is.read((char*)(&strh), sizeof(AviStreamHeader));
+ return is;
+}
+inline VideoInputStream& operator >> (VideoInputStream& is, BitmapInfoHeader& bmph)
+{
+ is.read((char*)(&bmph), sizeof(BitmapInfoHeader));
+ return is;
+}
+inline VideoInputStream& operator >> (VideoInputStream& is, AviIndex& idx1)
+{
+ is.read((char*)(&idx1), sizeof(idx1));
+ return is;
+}
+
+inline VideoInputStream& operator >> (VideoInputStream& is, RiffChunk& riff_chunk)
+{
+ is.read((char*)(&riff_chunk), sizeof(riff_chunk));
+ return is;
+}
+
+inline VideoInputStream& operator >> (VideoInputStream& is, RiffList& riff_list)
+{
+ is.read((char*)(&riff_list), sizeof(riff_list));
+ return is;
+}
+
+static const int AVIH_STRH_SIZE = 56;
+static const int STRF_SIZE = 40;
+static const int AVI_DWFLAG = 0x00000910;
+static const int AVI_DWSCALE = 1;
+static const int AVI_DWQUALITY = -1;
+static const int JUNK_SEEK = 4096;
+static const int AVIIF_KEYFRAME = 0x10;
+static const int MAX_BYTES_PER_SEC = 99999999;
+static const int SUG_BUFFER_SIZE = 1048576;
+
+String fourccToString(unsigned int fourcc)
+{
+ return format("%c%c%c%c", fourcc & 255, (fourcc >> 8) & 255, (fourcc >> 16) & 255, (fourcc >> 24) & 255);
+}
+
+VideoInputStream::VideoInputStream(): m_is_valid(false), m_f(0)
+{
+ m_fname = String();
+}
+
+VideoInputStream::VideoInputStream(const String& filename): m_is_valid(false), m_f(0)
+{
+ m_fname = filename;
+ open(filename);
+}
+
+bool VideoInputStream::isOpened() const
+{
+ return m_f != 0;
+}
+
+bool VideoInputStream::open(const String& filename)
+{
+ close();
+
+ m_f = fopen(filename.c_str(), "rb");
+
+ m_is_valid = isOpened();
+
+ return m_is_valid;
+}
+
+void VideoInputStream::close()
+{
+ if(isOpened())
+ {
+ m_is_valid = false;
+
+ fclose(m_f);
+ m_f = 0;
+ }
+}
+
+VideoInputStream& VideoInputStream::read(char* buf, unsigned long long int count)
+{
+ if(isOpened())
+ {
+ m_is_valid = (count == fread((void*)buf, 1, (size_t)count, m_f));
+ }
+
+ return *this;
+}
+
+VideoInputStream& VideoInputStream::seekg(unsigned long long int pos)
+{
+ m_is_valid = (fseek(m_f, (long)pos, SEEK_SET) == 0);
+
+ return *this;
+}
+
+unsigned long long int VideoInputStream::tellg()
+{
+ return ftell(m_f);
+}
+
+VideoInputStream::operator bool()
+{
+ return m_is_valid;
+}
+
+VideoInputStream& VideoInputStream::operator=(const VideoInputStream& stream)
+{
+ if (this != &stream) {
+ m_fname = stream.m_fname;
+ // m_f = stream.m_f;
+ open(m_fname);
+ }
+ return *this;
+}
+
+VideoInputStream::~VideoInputStream()
+{
+ close();
+}
+
+AVIReadContainer::AVIReadContainer(): 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)
+{
+ m_file_stream = makePtr<VideoInputStream>();
+}
+
+void AVIReadContainer::initStream(const String &filename)
+{
+ m_file_stream = makePtr<VideoInputStream>(filename);
+}
+
+void AVIReadContainer::initStream(Ptr<VideoInputStream> m_file_stream_)
+{
+ m_file_stream = m_file_stream_;
+}
+
+void AVIReadContainer::close()
+{
+ m_file_stream->close();
+}
+
+bool AVIReadContainer::parseIndex(unsigned int index_size, frame_list& in_frame_list)
+{
+ unsigned long long int index_end = m_file_stream->tellg();
+ index_end += index_size;
+ bool result = false;
+
+ while(m_file_stream && (m_file_stream->tellg() < index_end))
+ {
+ AviIndex idx1;
+ *m_file_stream >> idx1;
+
+ if(idx1.ckid == m_stream_id)
+ {
+ unsigned long long int 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 AVIReadContainer::parseStrl(char stream_id, Codecs codec_)
+{
+ RiffChunk strh;
+ *m_file_stream >> strh;
+
+ if(m_file_stream && strh.m_four_cc == STRH_CC)
+ {
+ unsigned long long int next_strl_list = m_file_stream->tellg();
+ next_strl_list += strh.m_size;
+
+ AviStreamHeader strm_hdr;
+ *m_file_stream >> strm_hdr;
+
+ if (codec_ == MJPEG)
+ {
+ if(strm_hdr.fccType == VIDS_CC && strm_hdr.fccHandler == MJPG_CC)
+ {
+ char first_digit = (stream_id/10) + '0';
+ char 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 AVIReadContainer::skipJunk(RiffChunk& chunk)
+{
+ if(chunk.m_four_cc == JUNK_CC)
+ {
+ m_file_stream->seekg(m_file_stream->tellg() + chunk.m_size);
+ *m_file_stream >> chunk;
+ }
+}
+
+void AVIReadContainer::skipJunk(RiffList& list)
+{
+ if(list.m_riff_or_list_cc == JUNK_CC)
+ {
+ //JUNK chunk is 4 bytes less than LIST
+ m_file_stream->seekg(m_file_stream->tellg() + list.m_size - 4);
+ *m_file_stream >> list;
+ }
+}
+
+bool AVIReadContainer::parseHdrlList(Codecs codec_)
+{
+ bool result = false;
+
+ RiffChunk avih;
+ *m_file_stream >> avih;
+
+ if(m_file_stream && avih.m_four_cc == AVIH_CC)
+ {
+ unsigned long long int next_strl_list = m_file_stream->tellg();
+ next_strl_list += avih.m_size;
+
+ AviMainHeader avi_hdr;
+ *m_file_stream >> avi_hdr;
+
+ if(m_file_stream)
+ {
+ m_is_indx_present = ((avi_hdr.dwFlags & 0x10) != 0);
+ DWORD number_of_streams = avi_hdr.dwStreams;
+ CV_Assert(number_of_streams < 0xFF);
+ m_width = avi_hdr.dwWidth;
+ m_height = avi_hdr.dwHeight;
+
+ //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)
+ {
+ m_file_stream->seekg(next_strl_list);
+ RiffList strl_list;
+ *m_file_stream >> strl_list;
+
+ if( m_file_stream && strl_list.m_riff_or_list_cc == LIST_CC && strl_list.m_list_type_cc == STRL_CC )
+ {
+ next_strl_list = m_file_stream->tellg();
+ //RiffList::m_size includes fourCC field which we have already read
+ next_strl_list += (strl_list.m_size - 4);
+
+ result = parseStrl((char)i, codec_);
+ }
+ else
+ {
+ printError(strl_list, STRL_CC);
+ }
+ }
+ }
+ }
+ else
+ {
+ printError(avih, AVIH_CC);
+ }
+
+ return result;
+}
+
+bool AVIReadContainer::parseAviWithFrameList(frame_list& in_frame_list, Codecs codec_)
+{
+ RiffList hdrl_list;
+ *m_file_stream >> hdrl_list;
+
+ if( m_file_stream && hdrl_list.m_riff_or_list_cc == LIST_CC && hdrl_list.m_list_type_cc == HDRL_CC )
+ {
+ unsigned long long int next_list = m_file_stream->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(codec_))
+ {
+ m_file_stream->seekg(next_list);
+
+ RiffList some_list;
+ *m_file_stream >> some_list;
+
+ //an optional section INFO
+ if(m_file_stream && some_list.m_riff_or_list_cc == LIST_CC && some_list.m_list_type_cc == INFO_CC)
+ {
+ next_list = m_file_stream->tellg();
+ //RiffList::m_size includes fourCC field which we have already read
+ next_list += (some_list.m_size - 4);
+ parseInfo();
+
+ m_file_stream->seekg(next_list);
+ *m_file_stream >> some_list;
+ }
+
+ //an optional section JUNK
+ skipJunk(some_list);
+
+ //we are expecting to find here movi list. Must present in avi
+ if(m_file_stream && 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 = m_file_stream->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
+ unsigned int indx_pos = (unsigned int)m_movi_start + 4;
+ indx_pos += (some_list.m_size - 4);
+ m_file_stream->seekg(indx_pos);
+
+ RiffChunk index_chunk;
+ *m_file_stream >> index_chunk;
+
+ if(m_file_stream && index_chunk.m_four_cc == IDX1_CC)
+ {
+ is_index_found = parseIndex(index_chunk.m_size, in_frame_list);
+ //we are not going anywhere else
+ }
+ else
+ {
+ printError(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_frame_list);
+
+ fprintf(stderr, "Failed to parse avi: index was not found\n");
+ //we are not going anywhere else
+ }
+ }
+ else
+ {
+ printError(some_list, MOVI_CC);
+ }
+ }
+ }
+ else
+ {
+ printError(hdrl_list, HDRL_CC);
+ }
+
+ return in_frame_list.size() > 0;
+}
+
+std::vector<char> AVIReadContainer::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[0]), chunk.m_size); // result.data() failed with MSVS2008
+
+ return result;
+}
+
+bool AVIReadContainer::parseRiff(frame_list &m_mjpeg_frames_)
+{
+ bool result = false;
+ while(*m_file_stream)
+ {
+ RiffList riff_list;
+
+ *m_file_stream >> riff_list;
+
+ if( *m_file_stream && 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)) )
+ {
+ unsigned long long int next_riff = m_file_stream->tellg();
+ //RiffList::m_size includes fourCC field which we have already read
+ next_riff += (riff_list.m_size - 4);
+
+ bool is_parsed = parseAvi(m_mjpeg_frames_, MJPEG);
+ result = result || is_parsed;
+ m_file_stream->seekg(next_riff);
+ }
+ else
+ {
+ break;
+ }
+ }
+ return result;
+}
+
+void AVIReadContainer::printError(RiffList &list, unsigned int expected_fourcc)
+{
+ if(!m_file_stream)
+ {
+ 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 AVIReadContainer::printError(RiffChunk &chunk, unsigned int expected_fourcc)
+{
+ if(!m_file_stream)
+ {
+ 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());
+ }
+}
+
+class BitStream
+{
+public:
+ BitStream();
+ ~BitStream() { close(); }
+
+ bool open(const String& filename);
+ bool isOpened() const { return m_f != 0; }
+ void close();
+
+ void writeBlock();
+ size_t getPos() const;
+ void putByte(int val);
+ void putBytes(const uchar* buf, int count);
+
+ void putShort(int val);
+ void putInt(int val);
+ void jputShort(int val);
+ void patchInt(int val, size_t pos);
+ void jput(unsigned currval);
+ void jflush(unsigned currval, int bitIdx);
+
+protected:
+ std::vector<uchar> m_buf;
+ uchar* m_start;
+ uchar* m_end;
+ uchar* m_current;
+ size_t m_pos;
+ bool m_is_opened;
+ FILE* m_f;
+};
+
+static const size_t DEFAULT_BLOCK_SIZE = (1 << 15);
+
+BitStream::BitStream()
+{
+ m_buf.resize(DEFAULT_BLOCK_SIZE + 1024);
+ m_start = &m_buf[0];
+ m_end = m_start + DEFAULT_BLOCK_SIZE;
+ m_is_opened = false;
+ m_f = 0;
+ m_current = 0;
+ m_pos = 0;
+}
+
+bool BitStream::open(const String& filename)
+{
+ close();
+ m_f = fopen(filename.c_str(), "wb");
+ if( !m_f )
+ return false;
+ m_current = m_start;
+ m_pos = 0;
+ return true;
+}
+
+void BitStream::close()
+{
+ writeBlock();
+ if( m_f )
+ fclose(m_f);
+ m_f = 0;
+}
+
+void BitStream::writeBlock()
+{
+ size_t wsz0 = m_current - m_start;
+ if( wsz0 > 0 && m_f )
+ {
+ size_t wsz = fwrite(m_start, 1, wsz0, m_f);
+ CV_Assert( wsz == wsz0 );
+ }
+ m_pos += wsz0;
+ m_current = m_start;
+}
+
+size_t BitStream::getPos() const {
+ return (size_t)(m_current - m_start) + m_pos;
+}
+
+void BitStream::putByte(int val)
+{
+ *m_current++ = (uchar)val;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+void BitStream::putBytes(const uchar* buf, int count)
+{
+ uchar* data = (uchar*)buf;
+ CV_Assert(m_f && data && m_current && count >= 0);
+ if( m_current >= m_end )
+ writeBlock();
+
+ while( count )
+ {
+ int l = (int)(m_end - m_current);
+
+ if (l > count)
+ l = count;
+
+ if( l > 0 )
+ {
+ memcpy(m_current, data, l);
+ m_current += l;
+ data += l;
+ count -= l;
+ }
+ if( m_current >= m_end )
+ writeBlock();
+ }
+}
+
+void BitStream::putShort(int val)
+{
+ m_current[0] = (uchar)val;
+ m_current[1] = (uchar)(val >> 8);
+ m_current += 2;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+void BitStream::putInt(int val)
+{
+ m_current[0] = (uchar)val;
+ m_current[1] = (uchar)(val >> 8);
+ m_current[2] = (uchar)(val >> 16);
+ m_current[3] = (uchar)(val >> 24);
+ m_current += 4;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+void BitStream::jputShort(int val)
+{
+ m_current[0] = (uchar)(val >> 8);
+ m_current[1] = (uchar)val;
+ m_current += 2;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+void BitStream::patchInt(int val, size_t pos)
+{
+ if( pos >= m_pos )
+ {
+ ptrdiff_t delta = pos - m_pos;
+ CV_Assert( delta < m_current - m_start );
+ m_start[delta] = (uchar)val;
+ m_start[delta+1] = (uchar)(val >> 8);
+ m_start[delta+2] = (uchar)(val >> 16);
+ m_start[delta+3] = (uchar)(val >> 24);
+ }
+ else
+ {
+ long fpos = ftell(m_f);
+ fseek(m_f, (long)pos, SEEK_SET);
+ uchar buf[] = { (uchar)val, (uchar)(val >> 8), (uchar)(val >> 16), (uchar)(val >> 24) };
+ fwrite(buf, 1, 4, m_f);
+ fseek(m_f, fpos, SEEK_SET);
+ }
+}
+
+void BitStream::jput(unsigned currval)
+{
+ uchar v;
+ uchar* ptr = m_current;
+ v = (uchar)(currval >> 24);
+ *ptr++ = v;
+ if( v == 255 )
+ *ptr++ = 0;
+ v = (uchar)(currval >> 16);
+ *ptr++ = v;
+ if( v == 255 )
+ *ptr++ = 0;
+ v = (uchar)(currval >> 8);
+ *ptr++ = v;
+ if( v == 255 )
+ *ptr++ = 0;
+ v = (uchar)currval;
+ *ptr++ = v;
+ if( v == 255 )
+ *ptr++ = 0;
+ m_current = ptr;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+void BitStream::jflush(unsigned currval, int bitIdx)
+{
+ uchar v;
+ uchar* ptr = m_current;
+ currval |= (1 << bitIdx)-1;
+ while( bitIdx < 32 )
+ {
+ v = (uchar)(currval >> 24);
+ *ptr++ = v;
+ if( v == 255 )
+ *ptr++ = 0;
+ currval <<= 8;
+ bitIdx += 8;
+ }
+ m_current = ptr;
+ if( m_current >= m_end )
+ writeBlock();
+}
+
+AVIWriteContainer::AVIWriteContainer() : strm(makePtr<BitStream>())
+{
+ outfps = 0;
+ height = 0;
+ width = 0;
+ channels = 0;
+ moviPointer = 0;
+ strm->close();
+}
+
+AVIWriteContainer::~AVIWriteContainer() {
+ strm->close();
+ frameOffset.clear();
+ frameSize.clear();
+ AVIChunkSizeIndex.clear();
+ frameNumIndexes.clear();
+}
+
+bool AVIWriteContainer::initContainer(const String& filename, double fps, Size size, bool iscolor)
+{
+ outfps = cvRound(fps);
+ width = size.width;
+ height = size.height;
+ channels = iscolor ? 3 : 1;
+ moviPointer = 0;
+ bool result = strm->open(filename);
+ return result;
+}
+
+void AVIWriteContainer::startWriteAVI(int stream_count)
+{
+ startWriteChunk(RIFF_CC);
+
+ strm->putInt(AVI_CC);
+
+ startWriteChunk(LIST_CC);
+
+ strm->putInt(HDRL_CC);
+ strm->putInt(AVIH_CC);
+ strm->putInt(AVIH_STRH_SIZE);
+ strm->putInt(cvRound(1e6 / outfps));
+ strm->putInt(MAX_BYTES_PER_SEC);
+ strm->putInt(0);
+ strm->putInt(AVI_DWFLAG);
+
+ frameNumIndexes.push_back(strm->getPos());
+
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(stream_count); // number of streams
+ strm->putInt(SUG_BUFFER_SIZE);
+ strm->putInt(width);
+ strm->putInt(height);
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(0);
+}
+
+void AVIWriteContainer::writeStreamHeader(Codecs codec_)
+{
+ // strh
+ startWriteChunk(LIST_CC);
+
+ strm->putInt(STRL_CC);
+ strm->putInt(STRH_CC);
+ strm->putInt(AVIH_STRH_SIZE);
+ strm->putInt(VIDS_CC);
+ switch (codec_) {
+ case MJPEG:
+ strm->putInt(MJPG_CC);
+ break;
+ }
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(AVI_DWSCALE);
+ strm->putInt(outfps);
+ strm->putInt(0);
+
+ frameNumIndexes.push_back(strm->getPos());
+
+ strm->putInt(0);
+ strm->putInt(SUG_BUFFER_SIZE);
+ strm->putInt(AVI_DWQUALITY);
+ strm->putInt(0);
+ strm->putShort(0);
+ strm->putShort(0);
+ strm->putShort(width);
+ strm->putShort(height);
+
+ // strf (use the BITMAPINFOHEADER for video)
+ startWriteChunk(STRF_CC);
+
+ strm->putInt(STRF_SIZE);
+ strm->putInt(width);
+ strm->putInt(height);
+ strm->putShort(1); // planes (1 means interleaved data (after decompression))
+
+ strm->putShort(8 * channels); // bits per pixel
+ switch (codec_) {
+ case MJPEG:
+ strm->putInt(MJPG_CC);
+ break;
+ }
+ strm->putInt(width * height * channels);
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(0);
+ strm->putInt(0);
+ // Must be indx chunk
+ endWriteChunk(); // end strf
+
+ endWriteChunk(); // end strl
+
+ // odml
+ startWriteChunk(LIST_CC);
+ strm->putInt(ODML_CC);
+ startWriteChunk(DMLH_CC);
+
+ frameNumIndexes.push_back(strm->getPos());
+
+ strm->putInt(0);
+ strm->putInt(0);
+
+ endWriteChunk(); // end dmlh
+ endWriteChunk(); // end odml
+
+ endWriteChunk(); // end hdrl
+
+ // JUNK
+ startWriteChunk(JUNK_CC);
+ size_t pos = strm->getPos();
+ for( ; pos < (size_t)JUNK_SEEK; pos += 4 )
+ strm->putInt(0);
+ endWriteChunk(); // end JUNK
+
+ // movi
+ startWriteChunk(LIST_CC);
+ moviPointer = strm->getPos();
+ strm->putInt(MOVI_CC);
+}
+
+void AVIWriteContainer::startWriteChunk(int fourcc)
+{
+ CV_Assert(fourcc != 0);
+ strm->putInt(fourcc);
+
+ AVIChunkSizeIndex.push_back(strm->getPos());
+ strm->putInt(0);
+}
+
+void AVIWriteContainer::endWriteChunk()
+{
+ if( !AVIChunkSizeIndex.empty() )
+ {
+ size_t currpos = strm->getPos();
+ size_t pospos = AVIChunkSizeIndex.back();
+ AVIChunkSizeIndex.pop_back();
+ int chunksz = (int)(currpos - (pospos + 4));
+ strm->patchInt(chunksz, pospos);
+ }
+}
+
+int AVIWriteContainer::getAVIIndex(int stream_number, StreamType strm_type) {
+ char strm_indx[2];
+ strm_indx[0] = '0' + static_cast<char>(stream_number / 10);
+ strm_indx[1] = '0' + static_cast<char>(stream_number % 10);
+
+ switch (strm_type) {
+ case db: return CV_FOURCC(strm_indx[0], strm_indx[1], 'd', 'b');
+ case dc: return CV_FOURCC(strm_indx[0], strm_indx[1], 'd', 'c');
+ case pc: return CV_FOURCC(strm_indx[0], strm_indx[1], 'p', 'c');
+ case wb: return CV_FOURCC(strm_indx[0], strm_indx[1], 'w', 'b');
+ default: return CV_FOURCC(strm_indx[0], strm_indx[1], 'd', 'b');
+ }
+}
+
+void AVIWriteContainer::writeIndex(int stream_number, StreamType strm_type)
+{
+ // old style AVI index. Must be Open-DML index
+ startWriteChunk(IDX1_CC);
+ int nframes = (int)frameOffset.size();
+ for( int i = 0; i < nframes; i++ )
+ {
+ strm->putInt(getAVIIndex(stream_number, strm_type));
+ strm->putInt(AVIIF_KEYFRAME);
+ strm->putInt((int)frameOffset[i]);
+ strm->putInt((int)frameSize[i]);
+ }
+ endWriteChunk(); // End idx1
+}
+
+void AVIWriteContainer::finishWriteAVI()
+{
+ int nframes = (int)frameOffset.size();
+ // Record frames numbers to AVI Header
+ while (!frameNumIndexes.empty())
+ {
+ size_t ppos = frameNumIndexes.back();
+ frameNumIndexes.pop_back();
+ strm->patchInt(nframes, ppos);
+ }
+ endWriteChunk(); // end RIFF
+}
+
+bool AVIWriteContainer::isOpenedStream() const { return strm->isOpened(); }
+
+size_t AVIWriteContainer::getStreamPos() const { return strm->getPos(); }
+
+void AVIWriteContainer::jputStreamShort(int val) { strm->jputShort(val); }
+
+void AVIWriteContainer::putStreamBytes(const uchar *buf, int count) { strm->putBytes( buf, count ); }
+
+void AVIWriteContainer::putStreamByte(int val) { strm->putByte(val); }
+
+void AVIWriteContainer::jputStream(unsigned currval) { strm->jput(currval); }
+
+void AVIWriteContainer::jflushStream(unsigned currval, int bitIdx) { strm->jflush(currval, bitIdx); }
+
+}
--- /dev/null
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#include "test_precomp.hpp"
+#include "opencv2/videoio/container_avi.private.hpp"
+#include <cstdio>
+
+using namespace cv;
+
+namespace opencv_test
+{
+
+TEST(videoio_avi, good_MJPG) {
+ String filename = BunnyParameters::getFilename(".mjpg.avi");
+ AVIReadContainer in;
+ in.initStream(filename);
+ frame_list frames;
+ ASSERT_TRUE(in.parseRiff(frames));
+ EXPECT_EQ(frames.size(), static_cast<unsigned>(BunnyParameters::getCount()));
+ EXPECT_EQ(in.getWidth(), static_cast<unsigned>(BunnyParameters::getWidth()));
+ EXPECT_EQ(in.getHeight(), static_cast<unsigned>(BunnyParameters::getHeight()));
+ EXPECT_EQ(in.getFps(), static_cast<unsigned>(BunnyParameters::getFps()));
+}
+
+TEST(videoio_avi, bad_MJPG) {
+ String filename = BunnyParameters::getFilename(".avi");
+ AVIReadContainer in;
+ in.initStream(filename);
+ frame_list frames;
+ EXPECT_FALSE(in.parseRiff(frames));
+ EXPECT_EQ(frames.size(), static_cast<unsigned>(0));
+}
+
+TEST(videoio_avi, basic)
+{
+ const String filename = cv::tempfile("test.avi");
+ const double fps = 100;
+ const Size sz(800, 600);
+ const size_t count = 10;
+ const uchar data[count] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA};
+ const Codecs codec = MJPEG;
+ {
+ AVIWriteContainer out;
+ ASSERT_TRUE(out.initContainer(filename, fps, sz, true));
+ ASSERT_TRUE(out.isOpenedStream());
+ EXPECT_EQ(out.getWidth(), sz.width);
+ EXPECT_EQ(out.getHeight(), sz.height);
+ EXPECT_EQ(out.getChannels(), 3);
+
+ out.startWriteAVI(1);
+ {
+ out.writeStreamHeader(codec); // starts LIST chunk
+ size_t chunkPointer = out.getStreamPos();
+ int avi_index = out.getAVIIndex(0, dc);
+ {
+ out.startWriteChunk(avi_index);
+ out.putStreamBytes(data, count);
+ size_t tempChunkPointer = out.getStreamPos();
+ size_t moviPointer = out.getMoviPointer();
+ out.pushFrameOffset(chunkPointer - moviPointer);
+ out.pushFrameSize(tempChunkPointer - chunkPointer - 8);
+ out.endWriteChunk();
+ }
+ out.endWriteChunk(); // ends LIST chunk
+ }
+ out.writeIndex(0, dc);
+ out.finishWriteAVI();
+ }
+ {
+ AVIReadContainer in;
+ in.initStream(filename);
+ frame_list frames;
+ ASSERT_TRUE(in.parseRiff(frames));
+ EXPECT_EQ(in.getFps(), fps);
+ EXPECT_EQ(in.getWidth(), static_cast<unsigned>(sz.width));
+ EXPECT_EQ(in.getHeight(), static_cast<unsigned>(sz.height));
+ ASSERT_EQ(frames.size(), static_cast<unsigned>(1));
+ std::vector<char> actual = in.readFrame(frames.begin());
+ ASSERT_EQ(actual.size(), count);
+ for (size_t i = 0; i < count; ++i)
+ EXPECT_EQ(actual.at(i), data[i]) << "at index " << i;
+ }
+ remove(filename.c_str());
+}
+
+}
#endif
}
+class BunnyParameters
+{
+public:
+ inline static int getWidth() { return 672; };
+ inline static int getHeight() { return 384; };
+ inline static int getFps() { return 24; };
+ inline static double getTime() { return 5.21; };
+ inline static int getCount() { return cvRound(getFps() * getTime()); };
+ inline static std::string getFilename(const std::string &ext)
+ {
+ return cvtest::TS::ptr()->get_data_path() + "video/big_buck_bunny" + ext;
+ }
+};
+
#endif
class Videoio_Bunny : public Videoio_Test_Base, public testing::TestWithParam<Backend_Type_Params>
{
+ BunnyParameters bunny_param;
public:
Videoio_Bunny()
{
ext = get<0>(GetParam());
apiPref = get<1>(GetParam());
-
- video_file = cvtest::TS::ptr()->get_data_path() + "video/big_buck_bunny." + ext;
+ video_file = BunnyParameters::getFilename(String(".") + ext);
}
void doFrameCountTest()
{
return;
}
- const int width_gt = 672;
- const int height_gt = 384;
- const int fps_gt = 24;
- const double time_gt = 5.21;
- const int count_gt = cvRound(fps_gt * time_gt); // 5.21 sec * 24 fps
-
- EXPECT_EQ(width_gt, cap.get(CAP_PROP_FRAME_WIDTH));
- EXPECT_EQ(height_gt, cap.get(CAP_PROP_FRAME_HEIGHT));
+ EXPECT_EQ(bunny_param.getWidth() , cap.get(CAP_PROP_FRAME_WIDTH));
+ EXPECT_EQ(bunny_param.getHeight(), cap.get(CAP_PROP_FRAME_HEIGHT));
double fps_prop = cap.get(CAP_PROP_FPS);
if (fps_prop > 0)
- EXPECT_NEAR(fps_prop, fps_gt, 1);
+ EXPECT_NEAR(fps_prop, bunny_param.getFps(), 1);
else
std::cout << "FPS is not available. SKIP check." << std::endl;
{
if (count_prop > 0)
{
- EXPECT_EQ(count_gt, count_prop);
+ EXPECT_EQ(bunny_param.getCount(), count_prop);
}
}
cap >> frame;
if (frame.empty())
break;
- EXPECT_EQ(width_gt, frame.cols);
- EXPECT_EQ(height_gt, frame.rows);
+ EXPECT_EQ(bunny_param.getWidth(), frame.cols);
+ EXPECT_EQ(bunny_param.getHeight(), frame.rows);
count_actual += 1;
}
if (count_prop > 0)
{
- EXPECT_NEAR(count_gt, count_actual, 1);
+ EXPECT_NEAR(bunny_param.getCount(), count_actual, 1);
}
else
std::cout << "Frames counter is not available. Actual frames: " << count_actual << ". SKIP check." << std::endl;