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