Merge pull request #10145 from allnes:vedeoio_avi_container
authorAlexander Alekhin <alexander.a.alekhin@gmail.com>
Thu, 15 Feb 2018 15:14:03 +0000 (15:14 +0000)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Thu, 15 Feb 2018 15:14:03 +0000 (15:14 +0000)
modules/videoio/CMakeLists.txt
modules/videoio/include/opencv2/videoio/container_avi.private.hpp [new file with mode: 0644]
modules/videoio/src/cap_mjpeg_decoder.cpp
modules/videoio/src/cap_mjpeg_encoder.cpp
modules/videoio/src/container_avi.cpp [new file with mode: 0644]
modules/videoio/test/test_container_avi.cpp [new file with mode: 0644]
modules/videoio/test/test_precomp.hpp
modules/videoio/test/test_video_io.cpp

index 0ded292..3562bfe 100644 (file)
@@ -19,12 +19,12 @@ endif()
 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
diff --git a/modules/videoio/include/opencv2/videoio/container_avi.private.hpp b/modules/videoio/include/opencv2/videoio/container_avi.private.hpp
new file mode 100644 (file)
index 0000000..212a892
--- /dev/null
@@ -0,0 +1,191 @@
+// 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
index 74a7bf4..cd2d841 100644 (file)
 //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:
@@ -702,12 +61,9 @@ 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;
 
@@ -779,23 +135,6 @@ double MotionJpegCapture::getProperty(int property) const
     }
 }
 
-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())
@@ -818,7 +157,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
 {
     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())
         {
@@ -840,6 +179,8 @@ MotionJpegCapture::~MotionJpegCapture()
 
 MotionJpegCapture::MotionJpegCapture(const String& filename)
 {
+    m_avi_container = makePtr<AVIReadContainer>();
+    m_avi_container->initStream(filename);
     open(filename);
 }
 
@@ -850,7 +191,7 @@ bool MotionJpegCapture::isOpened() const
 
 void MotionJpegCapture::close()
 {
-    m_file_stream.close();
+    m_avi_container->close();
     m_frame_iterator = m_mjpeg_frames.end();
 }
 
@@ -858,56 +199,23 @@ bool MotionJpegCapture::open(const String& filename)
 {
     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)
index c67bcae..60d7270 100644 (file)
 //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[] =
 {
@@ -79,279 +67,84 @@ 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
 {
@@ -593,11 +386,6 @@ public:
     {
         rawstream = false;
         nstripes = -1;
-        height = 0;
-        width = 0;
-        moviPointer = 0;
-        channels = 0;
-        outfps = 0;
         quality = 0;
     }
 
@@ -611,20 +399,15 @@ public:
 
     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)
@@ -639,222 +422,74 @@ public:
         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'
         }
     }
 
@@ -863,7 +498,10 @@ public:
         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.;
@@ -889,16 +527,12 @@ public:
     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))
@@ -1758,6 +1392,10 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
     }
 
     //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
@@ -1784,7 +1422,7 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
     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++ )
@@ -1792,9 +1430,9 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
         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++ )
@@ -1807,7 +1445,7 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
                 qval = 255;
             fdct_qtab[i][idx] = (short)(cvRound((1 << (postshift + 11)))/
                                 (qval*chroma_scale*idct_prescale[idx]));
-            strm.putByte( qval );
+            container.putStreamByte( qval );
         }
     }
 
@@ -1820,49 +1458,49 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
         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();
@@ -1877,18 +1515,18 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
 
     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);
 }
 
 }
diff --git a/modules/videoio/src/container_avi.cpp b/modules/videoio/src/container_avi.cpp
new file mode 100644 (file)
index 0000000..272f022
--- /dev/null
@@ -0,0 +1,1017 @@
+// 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); }
+
+}
diff --git a/modules/videoio/test/test_container_avi.cpp b/modules/videoio/test/test_container_avi.cpp
new file mode 100644 (file)
index 0000000..d4400e3
--- /dev/null
@@ -0,0 +1,87 @@
+// 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());
+}
+
+}
index 5d5ea03..8d9f5e0 100644 (file)
@@ -41,4 +41,18 @@ inline void generateFrame(int i, int FRAME_COUNT, cv::Mat & frame)
 #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
index c2b11e6..ef44aae 100644 (file)
@@ -151,13 +151,13 @@ typedef tuple<string, int> Backend_Type_Params;
 
 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()
     {
@@ -181,18 +181,12 @@ public:
             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;
 
@@ -204,7 +198,7 @@ public:
         {
             if (count_prop > 0)
             {
-                EXPECT_EQ(count_gt, count_prop);
+                EXPECT_EQ(bunny_param.getCount(), count_prop);
             }
         }
 
@@ -215,13 +209,13 @@ public:
             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;