Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2015, OpenCV Foundation, all rights reserved.
     14 // Third party copyrights are property of their respective owners.
     15 //
     16 // Redistribution and use in source and binary forms, with or without modification,
     17 // are permitted provided that the following conditions are met:
     18 //
     19 //   * Redistribution's of source code must retain the above copyright notice,
     20 //     this list of conditions and the following disclaimer.
     21 //
     22 //   * Redistribution's in binary form must reproduce the above copyright notice,
     23 //     this list of conditions and the following disclaimer in the documentation
     24 //     and/or other materials provided with the distribution.
     25 //
     26 //   * The name of Intel Corporation may not be used to endorse or promote products
     27 //     derived from this software without specific prior written permission.
     28 //
     29 // This software is provided by the copyright holders and contributors "as is" and
     30 // any express or implied warranties, including, but not limited to, the implied
     31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     32 // In no event shall the Intel Corporation or contributors be liable for any direct,
     33 // indirect, incidental, special, exemplary, or consequential damages
     34 // (including, but not limited to, procurement of substitute goods or services;
     35 // loss of use, data, or profits; or business interruption) however caused
     36 // and on any theory of liability, whether in contract, strict liability,
     37 // or tort (including negligence or otherwise) arising in any way out of
     38 // the use of this software, even if advised of the possibility of such damage.
     39 //
     40 //M*/
     41 
     42 #include "precomp.hpp"
     43 #include <deque>
     44 #include <stdint.h>
     45 
     46 namespace cv
     47 {
     48 
     49 const uint32_t RIFF_CC = CV_FOURCC('R','I','F','F');
     50 const uint32_t LIST_CC = CV_FOURCC('L','I','S','T');
     51 const uint32_t HDRL_CC = CV_FOURCC('h','d','r','l');
     52 const uint32_t AVIH_CC = CV_FOURCC('a','v','i','h');
     53 const uint32_t STRL_CC = CV_FOURCC('s','t','r','l');
     54 const uint32_t STRH_CC = CV_FOURCC('s','t','r','h');
     55 const uint32_t VIDS_CC = CV_FOURCC('v','i','d','s');
     56 const uint32_t MJPG_CC = CV_FOURCC('M','J','P','G');
     57 const uint32_t MOVI_CC = CV_FOURCC('m','o','v','i');
     58 const uint32_t IDX1_CC = CV_FOURCC('i','d','x','1');
     59 const uint32_t AVI_CC  = CV_FOURCC('A','V','I',' ');
     60 const uint32_t AVIX_CC = CV_FOURCC('A','V','I','X');
     61 const uint32_t JUNK_CC = CV_FOURCC('J','U','N','K');
     62 const uint32_t INFO_CC = CV_FOURCC('I','N','F','O');
     63 
     64 String fourccToString(uint32_t fourcc);
     65 
     66 String fourccToString(uint32_t fourcc)
     67 {
     68     return format("%c%c%c%c", fourcc & 255, (fourcc >> 8) & 255, (fourcc >> 16) & 255, (fourcc >> 24) & 255);
     69 }
     70 
     71 #ifndef DWORD
     72 typedef uint32_t DWORD;
     73 #endif
     74 #ifndef WORD
     75 typedef uint16_t WORD;
     76 #endif
     77 #ifndef LONG
     78 typedef int32_t  LONG;
     79 #endif
     80 
     81 #pragma pack(push, 1)
     82 struct AviMainHeader
     83 {
     84     DWORD dwMicroSecPerFrame;    //  The period between video frames
     85     DWORD dwMaxBytesPerSec;      //  Maximum data rate of the file
     86     DWORD dwReserved1;           // 0
     87     DWORD dwFlags;               //  0x10 AVIF_HASINDEX: The AVI file has an idx1 chunk containing an index at the end of the file.
     88     DWORD dwTotalFrames;         // Field of the main header specifies the total number of frames of data in file.
     89     DWORD dwInitialFrames;       // Is used for interleaved files
     90     DWORD dwStreams;             // Specifies the number of streams in the file.
     91     DWORD dwSuggestedBufferSize; // Field specifies the suggested buffer size forreading the file
     92     DWORD dwWidth;               // Fields specify the width of the AVIfile in pixels.
     93     DWORD dwHeight;              // Fields specify the height of the AVIfile in pixels.
     94     DWORD dwReserved[4];         // 0, 0, 0, 0
     95 };
     96 
     97 struct AviStreamHeader
     98 {
     99     uint32_t fccType;              // 'vids', 'auds', 'txts'...
    100     uint32_t fccHandler;           // "cvid", "DIB "
    101     DWORD dwFlags;               // 0
    102     DWORD dwPriority;            // 0
    103     DWORD dwInitialFrames;       // 0
    104     DWORD dwScale;               // 1
    105     DWORD dwRate;                // Fps (dwRate - frame rate for video streams)
    106     DWORD dwStart;               // 0
    107     DWORD dwLength;              // Frames number (playing time of AVI file as defined by scale and rate)
    108     DWORD dwSuggestedBufferSize; // For reading the stream
    109     DWORD dwQuality;             // -1 (encoding quality. If set to -1, drivers use the default quality value)
    110     DWORD dwSampleSize;          // 0 means that each frame is in its own chunk
    111     struct {
    112         short int left;
    113         short int top;
    114         short int right;
    115         short int bottom;
    116     } rcFrame;                // If stream has a different size than dwWidth*dwHeight(unused)
    117 };
    118 
    119 struct AviIndex
    120 {
    121     DWORD ckid;
    122     DWORD dwFlags;
    123     DWORD dwChunkOffset;
    124     DWORD dwChunkLength;
    125 };
    126 
    127 struct BitmapInfoHeader
    128 {
    129     DWORD biSize;                // Write header size of BITMAPINFO header structure
    130     LONG  biWidth;               // width in pixels
    131     LONG  biHeight;              // heigth in pixels
    132     WORD  biPlanes;              // Number of color planes in which the data is stored
    133     WORD  biBitCount;            // Number of bits per pixel
    134     DWORD biCompression;         // Type of compression used (uncompressed: NO_COMPRESSION=0)
    135     DWORD biSizeImage;           // Image Buffer. Quicktime needs 3 bytes also for 8-bit png
    136                                  //   (biCompression==NO_COMPRESSION)?0:xDim*yDim*bytesPerPixel;
    137     LONG  biXPelsPerMeter;       // Horizontal resolution in pixels per meter
    138     LONG  biYPelsPerMeter;       // Vertical resolution in pixels per meter
    139     DWORD biClrUsed;             // 256 (color table size; for 8-bit only)
    140     DWORD biClrImportant;        // Specifies that the first x colors of the color table. Are important to the DIB.
    141 };
    142 
    143 struct RiffChunk
    144 {
    145     uint32_t m_four_cc;
    146     uint32_t m_size;
    147 };
    148 
    149 struct RiffList
    150 {
    151     uint32_t m_riff_or_list_cc;
    152     uint32_t m_size;
    153     uint32_t m_list_type_cc;
    154 };
    155 
    156 #pragma pack(pop)
    157 
    158 class MjpegInputStream
    159 {
    160 public:
    161     MjpegInputStream();
    162     MjpegInputStream(const String& filename);
    163     ~MjpegInputStream();
    164     MjpegInputStream& read(char*, uint64_t);
    165     MjpegInputStream& seekg(uint64_t);
    166     uint64_t tellg();
    167     bool isOpened() const;
    168     bool open(const String& filename);
    169     void close();
    170     operator bool();
    171 
    172 private:
    173     bool    m_is_valid;
    174     FILE*   m_f;
    175 };
    176 
    177 MjpegInputStream::MjpegInputStream(): m_is_valid(false), m_f(0)
    178 {
    179 }
    180 
    181 MjpegInputStream::MjpegInputStream(const String& filename): m_is_valid(false), m_f(0)
    182 {
    183     open(filename);
    184 }
    185 
    186 bool MjpegInputStream::isOpened() const
    187 {
    188     return m_f != 0;
    189 }
    190 
    191 bool MjpegInputStream::open(const String& filename)
    192 {
    193     close();
    194 
    195     m_f = fopen(filename.c_str(), "rb");
    196 
    197     m_is_valid = isOpened();
    198 
    199     return m_is_valid;
    200 }
    201 
    202 void MjpegInputStream::close()
    203 {
    204     if(isOpened())
    205     {
    206         m_is_valid = false;
    207 
    208         fclose(m_f);
    209         m_f = 0;
    210     }
    211 }
    212 
    213 MjpegInputStream& MjpegInputStream::read(char* buf, uint64_t count)
    214 {
    215     if(isOpened())
    216     {
    217         m_is_valid = (count == fread((void*)buf, 1, (size_t)count, m_f));
    218     }
    219 
    220     return *this;
    221 }
    222 
    223 MjpegInputStream& MjpegInputStream::seekg(uint64_t pos)
    224 {
    225     m_is_valid = (fseek(m_f, (long)pos, SEEK_SET) == 0);
    226 
    227     return *this;
    228 }
    229 
    230 uint64_t MjpegInputStream::tellg()
    231 {
    232     return ftell(m_f);
    233 }
    234 
    235 MjpegInputStream::operator bool()
    236 {
    237     return m_is_valid;
    238 }
    239 
    240 MjpegInputStream::~MjpegInputStream()
    241 {
    242     close();
    243 }
    244 
    245 MjpegInputStream& operator >> (MjpegInputStream& is, AviMainHeader& avih);
    246 MjpegInputStream& operator >> (MjpegInputStream& is, AviStreamHeader& strh);
    247 MjpegInputStream& operator >> (MjpegInputStream& is, BitmapInfoHeader& bmph);
    248 MjpegInputStream& operator >> (MjpegInputStream& is, RiffList& riff_list);
    249 MjpegInputStream& operator >> (MjpegInputStream& is, RiffChunk& riff_chunk);
    250 MjpegInputStream& operator >> (MjpegInputStream& is, AviIndex& idx1);
    251 
    252 MjpegInputStream& operator >> (MjpegInputStream& is, AviMainHeader& avih)
    253 {
    254     is.read((char*)(&avih), sizeof(AviMainHeader));
    255     return is;
    256 }
    257 
    258 MjpegInputStream& operator >> (MjpegInputStream& is, AviStreamHeader& strh)
    259 {
    260     is.read((char*)(&strh), sizeof(AviStreamHeader));
    261     return is;
    262 }
    263 
    264 MjpegInputStream& operator >> (MjpegInputStream& is, BitmapInfoHeader& bmph)
    265 {
    266     is.read((char*)(&bmph), sizeof(BitmapInfoHeader));
    267     return is;
    268 }
    269 
    270 MjpegInputStream& operator >> (MjpegInputStream& is, RiffList& riff_list)
    271 {
    272     is.read((char*)(&riff_list), sizeof(riff_list));
    273     return is;
    274 }
    275 
    276 MjpegInputStream& operator >> (MjpegInputStream& is, RiffChunk& riff_chunk)
    277 {
    278     is.read((char*)(&riff_chunk), sizeof(riff_chunk));
    279     return is;
    280 }
    281 
    282 MjpegInputStream& operator >> (MjpegInputStream& is, AviIndex& idx1)
    283 {
    284     is.read((char*)(&idx1), sizeof(idx1));
    285     return is;
    286 }
    287 
    288 /*
    289 AVI struct:
    290 
    291 RIFF ('AVI '
    292       LIST ('hdrl'
    293             'avih'(<Main AVI Header>)
    294             LIST ('strl'
    295                   'strh'(<Stream header>)
    296                   'strf'(<Stream format>)
    297                   [ 'strd'(<Additional header data>) ]
    298                   [ 'strn'(<Stream name>) ]
    299                   [ 'indx'(<Odml index data>) ]
    300                   ...
    301                  )
    302             [LIST ('strl' ...)]
    303             [LIST ('strl' ...)]
    304             ...
    305             [LIST ('odml'
    306                   'dmlh'(<ODML header data>)
    307                   ...
    308                  )
    309             ]
    310             ...
    311            )
    312       [LIST ('INFO' ...)]
    313       [JUNK]
    314       LIST ('movi'
    315             {{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
    316                               {xxdb|xxdc|xxpc|xxwb}(<Data>)
    317                               {xxdb|xxdc|xxpc|xxwb}(<Data>)
    318                               ...
    319                              )
    320                ...
    321             }
    322             ...
    323            )
    324       ['idx1' (<AVI Index>) ]
    325      )
    326 
    327      {xxdb|xxdc|xxpc|xxwb}
    328      xx - stream number: 00, 01, 02, ...
    329      db - uncompressed video frame
    330      dc - commpressed video frame
    331      pc - palette change
    332      wb - audio frame
    333 
    334      JUNK section may pad any data section and must be ignored
    335 */
    336 
    337 typedef std::deque< std::pair<uint64_t, uint32_t> > frame_list;
    338 typedef frame_list::iterator frame_iterator;
    339 
    340 //Represents single MJPEG video stream within single AVI/AVIX entry
    341 //Multiple video streams within single AVI/AVIX entry are not supported
    342 //ODML index is not supported
    343 class AviMjpegStream
    344 {
    345 public:
    346     AviMjpegStream();
    347     //stores founded frames in m_frame_list which can be accessed via getFrames
    348     bool parseAvi(MjpegInputStream& in_str);
    349     //stores founded frames in in_frame_list. getFrames() would return empty list
    350     bool parseAvi(MjpegInputStream& in_str, frame_list& in_frame_list);
    351     size_t getFramesCount();
    352     frame_list& getFrames();
    353     uint32_t getWidth();
    354     uint32_t getHeight();
    355     double getFps();
    356 
    357 protected:
    358 
    359     bool parseAviWithFrameList(MjpegInputStream& in_str, frame_list& in_frame_list);
    360     void skipJunk(RiffChunk& chunk, MjpegInputStream& in_str);
    361     void skipJunk(RiffList& list, MjpegInputStream& in_str);
    362     bool parseHdrlList(MjpegInputStream& in_str);
    363     bool parseIndex(MjpegInputStream& in_str, uint32_t index_size, frame_list& in_frame_list);
    364     bool parseMovi(MjpegInputStream& in_str, frame_list& in_frame_list);
    365     bool parseStrl(MjpegInputStream& in_str, uint8_t stream_id);
    366     bool parseInfo(MjpegInputStream& in_str);
    367     void printError(MjpegInputStream& in_str, RiffList& list, uint32_t expected_fourcc);
    368     void printError(MjpegInputStream& in_str, RiffChunk& chunk, uint32_t expected_fourcc);
    369 
    370     uint32_t   m_stream_id;
    371     uint64_t   m_movi_start;
    372     uint64_t   m_movi_end;
    373     frame_list m_frame_list;
    374     uint32_t   m_width;
    375     uint32_t   m_height;
    376     double     m_fps;
    377     bool       m_is_indx_present;
    378 };
    379 
    380 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)
    381 {
    382 }
    383 
    384 size_t AviMjpegStream::getFramesCount()
    385 {
    386     return m_frame_list.size();
    387 }
    388 
    389 frame_list& AviMjpegStream::getFrames()
    390 {
    391     return m_frame_list;
    392 }
    393 
    394 uint32_t AviMjpegStream::getWidth()
    395 {
    396     return m_width;
    397 }
    398 
    399 uint32_t AviMjpegStream::getHeight()
    400 {
    401     return m_height;
    402 }
    403 
    404 double AviMjpegStream::getFps()
    405 {
    406     return m_fps;
    407 }
    408 
    409 void AviMjpegStream::printError(MjpegInputStream& in_str, RiffList& list, uint32_t expected_fourcc)
    410 {
    411     if(!in_str)
    412     {
    413         fprintf(stderr, "Unexpected end of file while searching for %s list\n", fourccToString(expected_fourcc).c_str());
    414     }
    415     else if(list.m_riff_or_list_cc != LIST_CC)
    416     {
    417         fprintf(stderr, "Unexpected element. Expected: %s. Got: %s.\n", fourccToString(LIST_CC).c_str(), fourccToString(list.m_riff_or_list_cc).c_str());
    418     }
    419     else
    420     {
    421         fprintf(stderr, "Unexpected list type. Expected: %s. Got: %s.\n", fourccToString(expected_fourcc).c_str(), fourccToString(list.m_list_type_cc).c_str());
    422     }
    423 }
    424 
    425 void AviMjpegStream::printError(MjpegInputStream& in_str, RiffChunk& chunk, uint32_t expected_fourcc)
    426 {
    427     if(!in_str)
    428     {
    429         fprintf(stderr, "Unexpected end of file while searching for %s chunk\n", fourccToString(expected_fourcc).c_str());
    430     }
    431     else
    432     {
    433         fprintf(stderr, "Unexpected element. Expected: %s. Got: %s.\n", fourccToString(expected_fourcc).c_str(), fourccToString(chunk.m_four_cc).c_str());
    434     }
    435 }
    436 
    437 
    438 bool AviMjpegStream::parseMovi(MjpegInputStream&, frame_list&)
    439 {
    440     //not implemented
    441     return true;
    442 }
    443 
    444 bool AviMjpegStream::parseInfo(MjpegInputStream&)
    445 {
    446     //not implemented
    447     return true;
    448 }
    449 
    450 bool AviMjpegStream::parseIndex(MjpegInputStream& in_str, uint32_t index_size, frame_list& in_frame_list)
    451 {
    452     uint64_t index_end = in_str.tellg();
    453     index_end += index_size;
    454     bool result = false;
    455 
    456     while(in_str && (in_str.tellg() < index_end))
    457     {
    458         AviIndex idx1;
    459         in_str >> idx1;
    460 
    461         if(idx1.ckid == m_stream_id)
    462         {
    463             uint64_t absolute_pos = m_movi_start + idx1.dwChunkOffset;
    464 
    465             if(absolute_pos < m_movi_end)
    466             {
    467                 in_frame_list.push_back(std::make_pair(absolute_pos, idx1.dwChunkLength));
    468             }
    469             else
    470             {
    471                 //unsupported case
    472                 fprintf(stderr, "Frame offset points outside movi section.\n");
    473             }
    474         }
    475 
    476         result = true;
    477     }
    478 
    479     return result;
    480 }
    481 
    482 bool AviMjpegStream::parseStrl(MjpegInputStream& in_str, uint8_t stream_id)
    483 {
    484     RiffChunk strh;
    485     in_str >> strh;
    486 
    487     if(in_str && strh.m_four_cc == STRH_CC)
    488     {
    489         uint64_t next_strl_list = in_str.tellg();
    490         next_strl_list += strh.m_size;
    491 
    492         AviStreamHeader strm_hdr;
    493         in_str >> strm_hdr;
    494 
    495         if(strm_hdr.fccType == VIDS_CC && strm_hdr.fccHandler == MJPG_CC)
    496         {
    497             uint8_t first_digit = (stream_id/10) + '0';
    498             uint8_t second_digit = (stream_id%10) + '0';
    499 
    500             if(m_stream_id == 0)
    501             {
    502                 m_stream_id = CV_FOURCC(first_digit, second_digit, 'd', 'c');
    503                 m_fps = double(strm_hdr.dwRate)/strm_hdr.dwScale;
    504             }
    505             else
    506             {
    507                 //second mjpeg video stream found which is not supported
    508                 fprintf(stderr, "More than one video stream found within AVI/AVIX list. Stream %c%cdc would be ignored\n", first_digit, second_digit);
    509             }
    510 
    511             return true;
    512         }
    513     }
    514 
    515     return false;
    516 }
    517 
    518 void AviMjpegStream::skipJunk(RiffChunk& chunk, MjpegInputStream& in_str)
    519 {
    520     if(chunk.m_four_cc == JUNK_CC)
    521     {
    522         in_str.seekg(in_str.tellg() + chunk.m_size);
    523         in_str >> chunk;
    524     }
    525 }
    526 
    527 void AviMjpegStream::skipJunk(RiffList& list, MjpegInputStream& in_str)
    528 {
    529     if(list.m_riff_or_list_cc == JUNK_CC)
    530     {
    531         //JUNK chunk is 4 bytes less than LIST
    532         in_str.seekg(in_str.tellg() + list.m_size - 4);
    533         in_str >> list;
    534     }
    535 }
    536 
    537 bool AviMjpegStream::parseHdrlList(MjpegInputStream& in_str)
    538 {
    539     bool result = false;
    540 
    541     RiffChunk avih;
    542     in_str >> avih;
    543 
    544     if(in_str && avih.m_four_cc == AVIH_CC)
    545     {
    546         uint64_t next_strl_list = in_str.tellg();
    547         next_strl_list += avih.m_size;
    548 
    549         AviMainHeader avi_hdr;
    550         in_str >> avi_hdr;
    551 
    552         if(in_str)
    553         {
    554             m_is_indx_present = ((avi_hdr.dwFlags & 0x10) != 0);
    555             DWORD number_of_streams = avi_hdr.dwStreams;
    556             m_width = avi_hdr.dwWidth;
    557             m_height = avi_hdr.dwWidth;
    558 
    559             //the number of strl lists must be equal to number of streams specified in main avi header
    560             for(DWORD i = 0; i < number_of_streams; ++i)
    561             {
    562                 in_str.seekg(next_strl_list);
    563                 RiffList strl_list;
    564                 in_str >> strl_list;
    565 
    566                 if( in_str && strl_list.m_riff_or_list_cc == LIST_CC && strl_list.m_list_type_cc == STRL_CC )
    567                 {
    568                     next_strl_list = in_str.tellg();
    569                     //RiffList::m_size includes fourCC field which we have already read
    570                     next_strl_list += (strl_list.m_size - 4);
    571 
    572                     result = parseStrl(in_str, (uint8_t)i);
    573                 }
    574                 else
    575                 {
    576                     printError(in_str, strl_list, STRL_CC);
    577                 }
    578             }
    579         }
    580     }
    581     else
    582     {
    583         printError(in_str, avih, AVIH_CC);
    584     }
    585 
    586     return result;
    587 }
    588 
    589 bool AviMjpegStream::parseAviWithFrameList(MjpegInputStream& in_str, frame_list& in_frame_list)
    590 {
    591     RiffList hdrl_list;
    592     in_str >> hdrl_list;
    593 
    594     if( in_str && hdrl_list.m_riff_or_list_cc == LIST_CC && hdrl_list.m_list_type_cc == HDRL_CC )
    595     {
    596         uint64_t next_list = in_str.tellg();
    597         //RiffList::m_size includes fourCC field which we have already read
    598         next_list += (hdrl_list.m_size - 4);
    599         //parseHdrlList sets m_is_indx_present flag which would be used later
    600         if(parseHdrlList(in_str))
    601         {
    602             in_str.seekg(next_list);
    603 
    604             RiffList some_list;
    605             in_str >> some_list;
    606 
    607             //an optional section INFO
    608             if(in_str && some_list.m_riff_or_list_cc == LIST_CC && some_list.m_list_type_cc == INFO_CC)
    609             {
    610                 next_list = in_str.tellg();
    611                 //RiffList::m_size includes fourCC field which we have already read
    612                 next_list += (some_list.m_size - 4);
    613                 parseInfo(in_str);
    614 
    615                 in_str.seekg(next_list);
    616                 in_str >> some_list;
    617             }
    618 
    619             //an optional section JUNK
    620             skipJunk(some_list, in_str);
    621 
    622             //we are expecting to find here movi list. Must present in avi
    623             if(in_str && some_list.m_riff_or_list_cc == LIST_CC && some_list.m_list_type_cc == MOVI_CC)
    624             {
    625                 bool is_index_found = false;
    626 
    627                 m_movi_start = in_str.tellg();
    628                 m_movi_start -= 4;
    629 
    630                 m_movi_end = m_movi_start + some_list.m_size;
    631                 //if m_is_indx_present is set to true we should find index
    632                 if(m_is_indx_present)
    633                 {
    634                     //we are expecting to find index section after movi list
    635                     uint32_t indx_pos = (uint32_t)m_movi_start + 4;
    636                     indx_pos += (some_list.m_size - 4);
    637                     in_str.seekg(indx_pos);
    638 
    639                     RiffChunk index_chunk;
    640                     in_str >> index_chunk;
    641 
    642                     if(in_str && index_chunk.m_four_cc == IDX1_CC)
    643                     {
    644                         is_index_found = parseIndex(in_str, index_chunk.m_size, in_frame_list);
    645                         //we are not going anywhere else
    646                     }
    647                     else
    648                     {
    649                         printError(in_str, index_chunk, IDX1_CC);
    650                     }
    651                 }
    652                 //index not present or we were not able to find it
    653                 //parsing movi list
    654                 if(!is_index_found)
    655                 {
    656                     //not implemented
    657                     parseMovi(in_str, in_frame_list);
    658 
    659                     fprintf(stderr, "Failed to parse avi: index was not found\n");
    660                     //we are not going anywhere else
    661                 }
    662             }
    663             else
    664             {
    665                 printError(in_str, some_list, MOVI_CC);
    666             }
    667         }
    668     }
    669     else
    670     {
    671         printError(in_str, hdrl_list, HDRL_CC);
    672     }
    673 
    674     return in_frame_list.size() > 0;
    675 }
    676 
    677 bool AviMjpegStream::parseAvi(MjpegInputStream& in_str, frame_list& in_frame_list)
    678 {
    679     return parseAviWithFrameList(in_str, in_frame_list);
    680 }
    681 
    682 bool AviMjpegStream::parseAvi(MjpegInputStream& in_str)
    683 {
    684     return parseAviWithFrameList(in_str, m_frame_list);
    685 }
    686 
    687 
    688 class MotionJpegCapture: public IVideoCapture
    689 {
    690 public:
    691     virtual ~MotionJpegCapture();
    692     virtual double getProperty(int) const;
    693     virtual bool setProperty(int, double);
    694     virtual bool grabFrame();
    695     virtual bool retrieveFrame(int, OutputArray);
    696     virtual bool isOpened() const;
    697     virtual int getCaptureDomain() { return CAP_ANY; } // Return the type of the capture object: CAP_VFW, etc...
    698     MotionJpegCapture(const String&);
    699 
    700     bool open(const String&);
    701     void close();
    702 protected:
    703 
    704     bool parseRiff(MjpegInputStream& in_str);
    705 
    706     inline uint64_t getFramePos() const;
    707     std::vector<char> readFrame(frame_iterator it);
    708 
    709     MjpegInputStream m_file_stream;
    710     bool             m_is_first_frame;
    711     frame_list       m_mjpeg_frames;
    712 
    713     frame_iterator   m_frame_iterator;
    714     Mat              m_current_frame;
    715 
    716     //frame width/height and fps could be different for
    717     //each frame/stream. At the moment we suppose that they
    718     //stays the same within single avi file.
    719     uint32_t         m_frame_width;
    720     uint32_t         m_frame_height;
    721     double           m_fps;
    722 };
    723 
    724 uint64_t MotionJpegCapture::getFramePos() const
    725 {
    726     if(m_is_first_frame)
    727         return 0;
    728 
    729     if(m_frame_iterator == m_mjpeg_frames.end())
    730         return m_mjpeg_frames.size();
    731 
    732     return m_frame_iterator - m_mjpeg_frames.begin() + 1;
    733 }
    734 
    735 bool MotionJpegCapture::setProperty(int property, double value)
    736 {
    737     if(property == CAP_PROP_POS_FRAMES)
    738     {
    739         if(int(value) == 0)
    740         {
    741             m_is_first_frame = true;
    742             m_frame_iterator = m_mjpeg_frames.end();
    743             return true;
    744         }
    745         else if(m_mjpeg_frames.size() > value)
    746         {
    747             m_frame_iterator = m_mjpeg_frames.begin() + int(value - 1);
    748             m_is_first_frame = false;
    749             return true;
    750         }
    751     }
    752 
    753     return false;
    754 }
    755 
    756 double MotionJpegCapture::getProperty(int property) const
    757 {
    758     switch(property)
    759     {
    760         case CAP_PROP_POS_FRAMES:
    761             return (double)getFramePos();
    762         case CAP_PROP_POS_AVI_RATIO:
    763             return double(getFramePos())/m_mjpeg_frames.size();
    764         case CAP_PROP_FRAME_WIDTH:
    765             return (double)m_frame_width;
    766         case CAP_PROP_FRAME_HEIGHT:
    767             return (double)m_frame_height;
    768         case CAP_PROP_FPS:
    769             return m_fps;
    770         case CAP_PROP_FOURCC:
    771             return (double)CV_FOURCC('M','J','P','G');
    772         case CAP_PROP_FRAME_COUNT:
    773             return (double)m_mjpeg_frames.size();
    774         case CAP_PROP_FORMAT:
    775             return 0;
    776         default:
    777             return 0;
    778     }
    779 }
    780 
    781 std::vector<char> MotionJpegCapture::readFrame(frame_iterator it)
    782 {
    783     m_file_stream.seekg(it->first);
    784 
    785     RiffChunk chunk;
    786     m_file_stream >> chunk;
    787 
    788     std::vector<char> result;
    789 
    790     result.reserve(chunk.m_size);
    791     result.resize(chunk.m_size);
    792 
    793     m_file_stream.read(result.data(), chunk.m_size);
    794 
    795     return result;
    796 }
    797 
    798 bool MotionJpegCapture::grabFrame()
    799 {
    800     if(isOpened())
    801     {
    802         if(m_is_first_frame)
    803         {
    804             m_is_first_frame = false;
    805             m_frame_iterator = m_mjpeg_frames.begin();
    806         }
    807         else
    808         {
    809             ++m_frame_iterator;
    810         }
    811     }
    812 
    813     return m_frame_iterator != m_mjpeg_frames.end();
    814 }
    815 
    816 bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
    817 {
    818     if(m_frame_iterator != m_mjpeg_frames.end())
    819     {
    820         std::vector<char> data = readFrame(m_frame_iterator);
    821 
    822         if(data.size())
    823         {
    824             m_current_frame = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR);
    825         }
    826 
    827         m_current_frame.copyTo(output_frame);
    828 
    829         return true;
    830     }
    831 
    832     return false;
    833 }
    834 
    835 MotionJpegCapture::~MotionJpegCapture()
    836 {
    837     close();
    838 }
    839 
    840 MotionJpegCapture::MotionJpegCapture(const String& filename)
    841 {
    842     open(filename);
    843 }
    844 
    845 bool MotionJpegCapture::isOpened() const
    846 {
    847     return m_mjpeg_frames.size() > 0;
    848 }
    849 
    850 void MotionJpegCapture::close()
    851 {
    852     m_file_stream.close();
    853     m_frame_iterator = m_mjpeg_frames.end();
    854 }
    855 
    856 bool MotionJpegCapture::open(const String& filename)
    857 {
    858     close();
    859 
    860     m_file_stream.open(filename);
    861 
    862     m_frame_iterator = m_mjpeg_frames.end();
    863     m_is_first_frame = true;
    864 
    865     if(!parseRiff(m_file_stream))
    866     {
    867         close();
    868     }
    869 
    870     return isOpened();
    871 }
    872 
    873 
    874 bool MotionJpegCapture::parseRiff(MjpegInputStream& in_str)
    875 {
    876     bool result = false;
    877     while(in_str)
    878     {
    879         RiffList riff_list;
    880 
    881         in_str >> riff_list;
    882 
    883         if( in_str && riff_list.m_riff_or_list_cc == RIFF_CC &&
    884             ((riff_list.m_list_type_cc == AVI_CC) | (riff_list.m_list_type_cc == AVIX_CC)) )
    885         {
    886             uint64_t next_riff = in_str.tellg();
    887             //RiffList::m_size includes fourCC field which we have already read
    888             next_riff += (riff_list.m_size - 4);
    889 
    890             AviMjpegStream mjpeg_video_stream;
    891             bool is_parsed = mjpeg_video_stream.parseAvi(in_str, m_mjpeg_frames);
    892             result = result || is_parsed;
    893 
    894             if(is_parsed)
    895             {
    896                 m_frame_width = mjpeg_video_stream.getWidth();
    897                 m_frame_height = mjpeg_video_stream.getHeight();
    898                 m_fps = mjpeg_video_stream.getFps();
    899             }
    900 
    901             in_str.seekg(next_riff);
    902         }
    903         else
    904         {
    905             break;
    906         }
    907     }
    908 
    909     return result;
    910 }
    911 
    912 Ptr<IVideoCapture> createMotionJpegCapture(const String& filename)
    913 {
    914     Ptr<MotionJpegCapture> mjdecoder(new MotionJpegCapture(filename));
    915     if( mjdecoder->isOpened() )
    916         return mjdecoder;
    917     return Ptr<MotionJpegCapture>();
    918 }
    919 
    920 }
    921