• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
fourccToString(uint32_t fourcc)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 
MjpegInputStream()177 MjpegInputStream::MjpegInputStream(): m_is_valid(false), m_f(0)
178 {
179 }
180 
MjpegInputStream(const String & filename)181 MjpegInputStream::MjpegInputStream(const String& filename): m_is_valid(false), m_f(0)
182 {
183     open(filename);
184 }
185 
isOpened() const186 bool MjpegInputStream::isOpened() const
187 {
188     return m_f != 0;
189 }
190 
open(const String & filename)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 
close()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 
read(char * buf,uint64_t count)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 
seekg(uint64_t pos)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 
tellg()230 uint64_t MjpegInputStream::tellg()
231 {
232     return ftell(m_f);
233 }
234 
operator bool()235 MjpegInputStream::operator bool()
236 {
237     return m_is_valid;
238 }
239 
~MjpegInputStream()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 
operator >>(MjpegInputStream & is,AviMainHeader & avih)252 MjpegInputStream& operator >> (MjpegInputStream& is, AviMainHeader& avih)
253 {
254     is.read((char*)(&avih), sizeof(AviMainHeader));
255     return is;
256 }
257 
operator >>(MjpegInputStream & is,AviStreamHeader & strh)258 MjpegInputStream& operator >> (MjpegInputStream& is, AviStreamHeader& strh)
259 {
260     is.read((char*)(&strh), sizeof(AviStreamHeader));
261     return is;
262 }
263 
operator >>(MjpegInputStream & is,BitmapInfoHeader & bmph)264 MjpegInputStream& operator >> (MjpegInputStream& is, BitmapInfoHeader& bmph)
265 {
266     is.read((char*)(&bmph), sizeof(BitmapInfoHeader));
267     return is;
268 }
269 
operator >>(MjpegInputStream & is,RiffList & riff_list)270 MjpegInputStream& operator >> (MjpegInputStream& is, RiffList& riff_list)
271 {
272     is.read((char*)(&riff_list), sizeof(riff_list));
273     return is;
274 }
275 
operator >>(MjpegInputStream & is,RiffChunk & riff_chunk)276 MjpegInputStream& operator >> (MjpegInputStream& is, RiffChunk& riff_chunk)
277 {
278     is.read((char*)(&riff_chunk), sizeof(riff_chunk));
279     return is;
280 }
281 
operator >>(MjpegInputStream & is,AviIndex & idx1)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 
AviMjpegStream()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 
getFramesCount()384 size_t AviMjpegStream::getFramesCount()
385 {
386     return m_frame_list.size();
387 }
388 
getFrames()389 frame_list& AviMjpegStream::getFrames()
390 {
391     return m_frame_list;
392 }
393 
getWidth()394 uint32_t AviMjpegStream::getWidth()
395 {
396     return m_width;
397 }
398 
getHeight()399 uint32_t AviMjpegStream::getHeight()
400 {
401     return m_height;
402 }
403 
getFps()404 double AviMjpegStream::getFps()
405 {
406     return m_fps;
407 }
408 
printError(MjpegInputStream & in_str,RiffList & list,uint32_t expected_fourcc)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 
printError(MjpegInputStream & in_str,RiffChunk & chunk,uint32_t expected_fourcc)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 
parseMovi(MjpegInputStream &,frame_list &)438 bool AviMjpegStream::parseMovi(MjpegInputStream&, frame_list&)
439 {
440     //not implemented
441     return true;
442 }
443 
parseInfo(MjpegInputStream &)444 bool AviMjpegStream::parseInfo(MjpegInputStream&)
445 {
446     //not implemented
447     return true;
448 }
449 
parseIndex(MjpegInputStream & in_str,uint32_t index_size,frame_list & in_frame_list)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 
parseStrl(MjpegInputStream & in_str,uint8_t stream_id)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 
skipJunk(RiffChunk & chunk,MjpegInputStream & in_str)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 
skipJunk(RiffList & list,MjpegInputStream & in_str)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 
parseHdrlList(MjpegInputStream & in_str)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 
parseAviWithFrameList(MjpegInputStream & in_str,frame_list & in_frame_list)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 
parseAvi(MjpegInputStream & in_str,frame_list & in_frame_list)677 bool AviMjpegStream::parseAvi(MjpegInputStream& in_str, frame_list& in_frame_list)
678 {
679     return parseAviWithFrameList(in_str, in_frame_list);
680 }
681 
parseAvi(MjpegInputStream & in_str)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;
getCaptureDomain()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 
getFramePos() const724 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 
setProperty(int property,double value)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 
getProperty(int property) const756 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 
readFrame(frame_iterator it)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 
grabFrame()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 
retrieveFrame(int,OutputArray output_frame)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 
~MotionJpegCapture()835 MotionJpegCapture::~MotionJpegCapture()
836 {
837     close();
838 }
839 
MotionJpegCapture(const String & filename)840 MotionJpegCapture::MotionJpegCapture(const String& filename)
841 {
842     open(filename);
843 }
844 
isOpened() const845 bool MotionJpegCapture::isOpened() const
846 {
847     return m_mjpeg_frames.size() > 0;
848 }
849 
close()850 void MotionJpegCapture::close()
851 {
852     m_file_stream.close();
853     m_frame_iterator = m_mjpeg_frames.end();
854 }
855 
open(const String & filename)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 
parseRiff(MjpegInputStream & in_str)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 
createMotionJpegCapture(const String & filename)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