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