Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
30b33a44
Commit
30b33a44
authored
Feb 15, 2018
by
Alexander Alekhin
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #10145 from allnes:vedeoio_avi_container
parents
cff79609
df8f6187
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1459 additions
and
1210 deletions
+1459
-1210
CMakeLists.txt
modules/videoio/CMakeLists.txt
+1
-1
container_avi.private.hpp
...videoio/include/opencv2/videoio/container_avi.private.hpp
+191
-0
cap_mjpeg_decoder.cpp
modules/videoio/src/cap_mjpeg_decoder.cpp
+14
-706
cap_mjpeg_encoder.cpp
modules/videoio/src/cap_mjpeg_encoder.cpp
+126
-488
container_avi.cpp
modules/videoio/src/container_avi.cpp
+1017
-0
test_container_avi.cpp
modules/videoio/test/test_container_avi.cpp
+87
-0
test_precomp.hpp
modules/videoio/test/test_precomp.hpp
+14
-0
test_video_io.cpp
modules/videoio/test/test_video_io.cpp
+9
-15
No files found.
modules/videoio/CMakeLists.txt
View file @
30b33a44
...
...
@@ -19,12 +19,12 @@ endif()
set
(
videoio_hdrs
${
CMAKE_CURRENT_LIST_DIR
}
/src/precomp.hpp
)
set
(
videoio_srcs
${
CMAKE_CURRENT_LIST_DIR
}
/src/cap.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/src/cap_images.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/src/cap_mjpeg_encoder.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/src/cap_mjpeg_decoder.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/src/container_avi.cpp
)
file
(
GLOB videoio_ext_hdrs
...
...
modules/videoio/include/opencv2/videoio/container_avi.private.hpp
0 → 100644
View file @
30b33a44
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef CONTAINER_AVI_HPP
#define CONTAINER_AVI_HPP
#ifndef __OPENCV_BUILD
# error this is a private header which should not be used from outside of the OpenCV library
#endif
#include "opencv2/core/cvdef.h"
#include "opencv2/videoio/videoio_c.h"
#include <deque>
namespace
cv
{
/*
AVI struct:
RIFF ('AVI '
LIST ('hdrl'
'avih'(<Main AVI Header>)
LIST ('strl'
'strh'(<Stream header>)
'strf'(<Stream format>)
[ 'strd'(<Additional header data>) ]
[ 'strn'(<Stream name>) ]
[ 'indx'(<Odml index data>) ]
...
)
[LIST ('strl' ...)]
[LIST ('strl' ...)]
...
[LIST ('odml'
'dmlh'(<ODML header data>)
...
)
]
...
)
[LIST ('INFO' ...)]
[JUNK]
LIST ('movi'
{{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
{xxdb|xxdc|xxpc|xxwb}(<Data>)
{xxdb|xxdc|xxpc|xxwb}(<Data>)
...
)
...
}
...
)
['idx1' (<AVI Index>) ]
)
{xxdb|xxdc|xxpc|xxwb}
xx - stream number: 00, 01, 02, ...
db - uncompressed video frame
dc - commpressed video frame
pc - palette change
wb - audio frame
JUNK section may pad any data section and must be ignored
*/
typedef
std
::
deque
<
std
::
pair
<
uint64_t
,
uint32_t
>
>
frame_list
;
typedef
frame_list
::
iterator
frame_iterator
;
struct
RiffChunk
;
struct
RiffList
;
class
VideoInputStream
;
enum
Codecs
{
MJPEG
};
//Represents single MJPEG video stream within single AVI/AVIX entry
//Multiple video streams within single AVI/AVIX entry are not supported
//ODML index is not supported
class
CV_EXPORTS
AVIReadContainer
{
public
:
AVIReadContainer
();
void
initStream
(
const
String
&
filename
);
void
initStream
(
Ptr
<
VideoInputStream
>
m_file_stream_
);
void
close
();
//stores founded frames in m_frame_list which can be accessed via getFrames
bool
parseAvi
(
Codecs
codec_
)
{
return
parseAviWithFrameList
(
m_frame_list
,
codec_
);
}
//stores founded frames in in_frame_list. getFrames() would return empty list
bool
parseAvi
(
frame_list
&
in_frame_list
,
Codecs
codec_
)
{
return
parseAviWithFrameList
(
in_frame_list
,
codec_
);
}
size_t
getFramesCount
()
{
return
m_frame_list
.
size
();
}
frame_list
&
getFrames
()
{
return
m_frame_list
;
}
unsigned
int
getWidth
()
{
return
m_width
;
}
unsigned
int
getHeight
()
{
return
m_height
;
}
double
getFps
()
{
return
m_fps
;
}
std
::
vector
<
char
>
readFrame
(
frame_iterator
it
);
bool
parseRiff
(
frame_list
&
m_mjpeg_frames
);
protected
:
bool
parseAviWithFrameList
(
frame_list
&
in_frame_list
,
Codecs
codec_
);
void
skipJunk
(
RiffChunk
&
chunk
);
void
skipJunk
(
RiffList
&
list
);
bool
parseHdrlList
(
Codecs
codec_
);
bool
parseIndex
(
unsigned
int
index_size
,
frame_list
&
in_frame_list
);
bool
parseMovi
(
frame_list
&
in_frame_list
)
{
//not implemented
in_frame_list
.
empty
();
return
true
;
}
bool
parseStrl
(
char
stream_id
,
Codecs
codec_
);
bool
parseInfo
()
{
//not implemented
return
true
;
}
void
printError
(
RiffList
&
list
,
unsigned
int
expected_fourcc
);
void
printError
(
RiffChunk
&
chunk
,
unsigned
int
expected_fourcc
);
Ptr
<
VideoInputStream
>
m_file_stream
;
unsigned
int
m_stream_id
;
unsigned
long
long
int
m_movi_start
;
unsigned
long
long
int
m_movi_end
;
frame_list
m_frame_list
;
unsigned
int
m_width
;
unsigned
int
m_height
;
double
m_fps
;
bool
m_is_indx_present
;
};
enum
{
COLORSPACE_GRAY
=
0
,
COLORSPACE_RGBA
=
1
,
COLORSPACE_BGR
=
2
,
COLORSPACE_YUV444P
=
3
};
enum
StreamType
{
db
,
dc
,
pc
,
wb
};
class
BitStream
;
// {xxdb|xxdc|xxpc|xxwb}
// xx - stream number: 00, 01, 02, ...
// db - uncompressed video frame
// dc - commpressed video frame
// pc - palette change
// wb - audio frame
class
CV_EXPORTS
AVIWriteContainer
{
public
:
AVIWriteContainer
();
~
AVIWriteContainer
();
bool
initContainer
(
const
String
&
filename
,
double
fps
,
Size
size
,
bool
iscolor
);
void
startWriteAVI
(
int
stream_count
);
void
writeStreamHeader
(
Codecs
codec_
);
void
startWriteChunk
(
int
fourcc
);
void
endWriteChunk
();
int
getAVIIndex
(
int
stream_number
,
StreamType
strm_type
);
void
writeIndex
(
int
stream_number
,
StreamType
strm_type
);
void
finishWriteAVI
();
bool
isOpenedStream
()
const
;
bool
isEmptyFrameOffset
()
const
{
return
frameOffset
.
empty
();
}
int
getWidth
()
const
{
return
width
;
}
int
getHeight
()
const
{
return
height
;
}
int
getChannels
()
const
{
return
channels
;
}
size_t
getMoviPointer
()
const
{
return
moviPointer
;
}
size_t
getStreamPos
()
const
;
void
pushFrameOffset
(
size_t
elem
)
{
frameOffset
.
push_back
(
elem
);
}
void
pushFrameSize
(
size_t
elem
)
{
frameSize
.
push_back
(
elem
);
}
bool
isEmptyFrameSize
()
const
{
return
frameSize
.
empty
();
}
size_t
atFrameSize
(
size_t
i
)
const
{
return
frameSize
[
i
];
}
size_t
countFrameSize
()
const
{
return
frameSize
.
size
();
}
void
jputStreamShort
(
int
val
);
void
putStreamBytes
(
const
uchar
*
buf
,
int
count
);
void
putStreamByte
(
int
val
);
void
jputStream
(
unsigned
currval
);
void
jflushStream
(
unsigned
currval
,
int
bitIdx
);
private
:
Ptr
<
BitStream
>
strm
;
int
outfps
;
int
width
,
height
,
channels
;
size_t
moviPointer
;
std
::
vector
<
size_t
>
frameOffset
,
frameSize
,
AVIChunkSizeIndex
,
frameNumIndexes
;
};
}
#endif //CONTAINER_AVI_HPP
modules/videoio/src/cap_mjpeg_decoder.cpp
View file @
30b33a44
...
...
@@ -40,652 +40,11 @@
//M*/
#include "precomp.hpp"
#include <deque>
#include <stdint.h>
#include "opencv2/videoio/container_avi.private.hpp"
namespace
cv
{
const
uint32_t
RIFF_CC
=
CV_FOURCC
(
'R'
,
'I'
,
'F'
,
'F'
);
const
uint32_t
LIST_CC
=
CV_FOURCC
(
'L'
,
'I'
,
'S'
,
'T'
);
const
uint32_t
HDRL_CC
=
CV_FOURCC
(
'h'
,
'd'
,
'r'
,
'l'
);
const
uint32_t
AVIH_CC
=
CV_FOURCC
(
'a'
,
'v'
,
'i'
,
'h'
);
const
uint32_t
STRL_CC
=
CV_FOURCC
(
's'
,
't'
,
'r'
,
'l'
);
const
uint32_t
STRH_CC
=
CV_FOURCC
(
's'
,
't'
,
'r'
,
'h'
);
const
uint32_t
VIDS_CC
=
CV_FOURCC
(
'v'
,
'i'
,
'd'
,
's'
);
const
uint32_t
MJPG_CC
=
CV_FOURCC
(
'M'
,
'J'
,
'P'
,
'G'
);
const
uint32_t
MOVI_CC
=
CV_FOURCC
(
'm'
,
'o'
,
'v'
,
'i'
);
const
uint32_t
IDX1_CC
=
CV_FOURCC
(
'i'
,
'd'
,
'x'
,
'1'
);
const
uint32_t
AVI_CC
=
CV_FOURCC
(
'A'
,
'V'
,
'I'
,
' '
);
const
uint32_t
AVIX_CC
=
CV_FOURCC
(
'A'
,
'V'
,
'I'
,
'X'
);
const
uint32_t
JUNK_CC
=
CV_FOURCC
(
'J'
,
'U'
,
'N'
,
'K'
);
const
uint32_t
INFO_CC
=
CV_FOURCC
(
'I'
,
'N'
,
'F'
,
'O'
);
String
fourccToString
(
uint32_t
fourcc
);
String
fourccToString
(
uint32_t
fourcc
)
{
return
format
(
"%c%c%c%c"
,
fourcc
&
255
,
(
fourcc
>>
8
)
&
255
,
(
fourcc
>>
16
)
&
255
,
(
fourcc
>>
24
)
&
255
);
}
#ifndef DWORD
typedef
uint32_t
DWORD
;
#endif
#ifndef WORD
typedef
uint16_t
WORD
;
#endif
#ifndef LONG
typedef
int32_t
LONG
;
#endif
#pragma pack(push, 1)
struct
AviMainHeader
{
DWORD
dwMicroSecPerFrame
;
// The period between video frames
DWORD
dwMaxBytesPerSec
;
// Maximum data rate of the file
DWORD
dwReserved1
;
// 0
DWORD
dwFlags
;
// 0x10 AVIF_HASINDEX: The AVI file has an idx1 chunk containing an index at the end of the file.
DWORD
dwTotalFrames
;
// Field of the main header specifies the total number of frames of data in file.
DWORD
dwInitialFrames
;
// Is used for interleaved files
DWORD
dwStreams
;
// Specifies the number of streams in the file.
DWORD
dwSuggestedBufferSize
;
// Field specifies the suggested buffer size forreading the file
DWORD
dwWidth
;
// Fields specify the width of the AVIfile in pixels.
DWORD
dwHeight
;
// Fields specify the height of the AVIfile in pixels.
DWORD
dwReserved
[
4
];
// 0, 0, 0, 0
};
struct
AviStreamHeader
{
uint32_t
fccType
;
// 'vids', 'auds', 'txts'...
uint32_t
fccHandler
;
// "cvid", "DIB "
DWORD
dwFlags
;
// 0
DWORD
dwPriority
;
// 0
DWORD
dwInitialFrames
;
// 0
DWORD
dwScale
;
// 1
DWORD
dwRate
;
// Fps (dwRate - frame rate for video streams)
DWORD
dwStart
;
// 0
DWORD
dwLength
;
// Frames number (playing time of AVI file as defined by scale and rate)
DWORD
dwSuggestedBufferSize
;
// For reading the stream
DWORD
dwQuality
;
// -1 (encoding quality. If set to -1, drivers use the default quality value)
DWORD
dwSampleSize
;
// 0 means that each frame is in its own chunk
struct
{
short
int
left
;
short
int
top
;
short
int
right
;
short
int
bottom
;
}
rcFrame
;
// If stream has a different size than dwWidth*dwHeight(unused)
};
struct
AviIndex
{
DWORD
ckid
;
DWORD
dwFlags
;
DWORD
dwChunkOffset
;
DWORD
dwChunkLength
;
};
struct
BitmapInfoHeader
{
DWORD
biSize
;
// Write header size of BITMAPINFO header structure
LONG
biWidth
;
// width in pixels
LONG
biHeight
;
// height in pixels
WORD
biPlanes
;
// Number of color planes in which the data is stored
WORD
biBitCount
;
// Number of bits per pixel
DWORD
biCompression
;
// Type of compression used (uncompressed: NO_COMPRESSION=0)
DWORD
biSizeImage
;
// Image Buffer. Quicktime needs 3 bytes also for 8-bit png
// (biCompression==NO_COMPRESSION)?0:xDim*yDim*bytesPerPixel;
LONG
biXPelsPerMeter
;
// Horizontal resolution in pixels per meter
LONG
biYPelsPerMeter
;
// Vertical resolution in pixels per meter
DWORD
biClrUsed
;
// 256 (color table size; for 8-bit only)
DWORD
biClrImportant
;
// Specifies that the first x colors of the color table. Are important to the DIB.
};
struct
RiffChunk
{
uint32_t
m_four_cc
;
uint32_t
m_size
;
};
struct
RiffList
{
uint32_t
m_riff_or_list_cc
;
uint32_t
m_size
;
uint32_t
m_list_type_cc
;
};
#pragma pack(pop)
class
MjpegInputStream
{
public
:
MjpegInputStream
();
MjpegInputStream
(
const
String
&
filename
);
~
MjpegInputStream
();
MjpegInputStream
&
read
(
char
*
,
uint64_t
);
MjpegInputStream
&
seekg
(
uint64_t
);
uint64_t
tellg
();
bool
isOpened
()
const
;
bool
open
(
const
String
&
filename
);
void
close
();
operator
bool
();
private
:
bool
m_is_valid
;
FILE
*
m_f
;
};
MjpegInputStream
::
MjpegInputStream
()
:
m_is_valid
(
false
),
m_f
(
0
)
{
}
MjpegInputStream
::
MjpegInputStream
(
const
String
&
filename
)
:
m_is_valid
(
false
),
m_f
(
0
)
{
open
(
filename
);
}
bool
MjpegInputStream
::
isOpened
()
const
{
return
m_f
!=
0
;
}
bool
MjpegInputStream
::
open
(
const
String
&
filename
)
{
close
();
m_f
=
fopen
(
filename
.
c_str
(),
"rb"
);
m_is_valid
=
isOpened
();
return
m_is_valid
;
}
void
MjpegInputStream
::
close
()
{
if
(
isOpened
())
{
m_is_valid
=
false
;
fclose
(
m_f
);
m_f
=
0
;
}
}
MjpegInputStream
&
MjpegInputStream
::
read
(
char
*
buf
,
uint64_t
count
)
{
if
(
isOpened
())
{
m_is_valid
=
(
count
==
fread
((
void
*
)
buf
,
1
,
(
size_t
)
count
,
m_f
));
}
return
*
this
;
}
MjpegInputStream
&
MjpegInputStream
::
seekg
(
uint64_t
pos
)
{
m_is_valid
=
(
fseek
(
m_f
,
(
long
)
pos
,
SEEK_SET
)
==
0
);
return
*
this
;
}
uint64_t
MjpegInputStream
::
tellg
()
{
return
ftell
(
m_f
);
}
MjpegInputStream
::
operator
bool
()
{
return
m_is_valid
;
}
MjpegInputStream
::~
MjpegInputStream
()
{
close
();
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviMainHeader
&
avih
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviStreamHeader
&
strh
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
BitmapInfoHeader
&
bmph
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
RiffList
&
riff_list
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
RiffChunk
&
riff_chunk
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviIndex
&
idx1
);
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviMainHeader
&
avih
)
{
is
.
read
((
char
*
)(
&
avih
),
sizeof
(
AviMainHeader
));
return
is
;
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviStreamHeader
&
strh
)
{
is
.
read
((
char
*
)(
&
strh
),
sizeof
(
AviStreamHeader
));
return
is
;
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
BitmapInfoHeader
&
bmph
)
{
is
.
read
((
char
*
)(
&
bmph
),
sizeof
(
BitmapInfoHeader
));
return
is
;
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
RiffList
&
riff_list
)
{
is
.
read
((
char
*
)(
&
riff_list
),
sizeof
(
riff_list
));
return
is
;
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
RiffChunk
&
riff_chunk
)
{
is
.
read
((
char
*
)(
&
riff_chunk
),
sizeof
(
riff_chunk
));
return
is
;
}
MjpegInputStream
&
operator
>>
(
MjpegInputStream
&
is
,
AviIndex
&
idx1
)
{
is
.
read
((
char
*
)(
&
idx1
),
sizeof
(
idx1
));
return
is
;
}
/*
AVI struct:
RIFF ('AVI '
LIST ('hdrl'
'avih'(<Main AVI Header>)
LIST ('strl'
'strh'(<Stream header>)
'strf'(<Stream format>)
[ 'strd'(<Additional header data>) ]
[ 'strn'(<Stream name>) ]
[ 'indx'(<Odml index data>) ]
...
)
[LIST ('strl' ...)]
[LIST ('strl' ...)]
...
[LIST ('odml'
'dmlh'(<ODML header data>)
...
)
]
...
)
[LIST ('INFO' ...)]
[JUNK]
LIST ('movi'
{{xxdb|xxdc|xxpc|xxwb}(<Data>) | LIST ('rec '
{xxdb|xxdc|xxpc|xxwb}(<Data>)
{xxdb|xxdc|xxpc|xxwb}(<Data>)
...
)
...
}
...
)
['idx1' (<AVI Index>) ]
)
{xxdb|xxdc|xxpc|xxwb}
xx - stream number: 00, 01, 02, ...
db - uncompressed video frame
dc - commpressed video frame
pc - palette change
wb - audio frame
JUNK section may pad any data section and must be ignored
*/
typedef
std
::
deque
<
std
::
pair
<
uint64_t
,
uint32_t
>
>
frame_list
;
typedef
frame_list
::
iterator
frame_iterator
;
//Represents single MJPEG video stream within single AVI/AVIX entry
//Multiple video streams within single AVI/AVIX entry are not supported
//ODML index is not supported
class
AviMjpegStream
{
public
:
AviMjpegStream
();
//stores founded frames in m_frame_list which can be accessed via getFrames
bool
parseAvi
(
MjpegInputStream
&
in_str
);
//stores founded frames in in_frame_list. getFrames() would return empty list
bool
parseAvi
(
MjpegInputStream
&
in_str
,
frame_list
&
in_frame_list
);
size_t
getFramesCount
();
frame_list
&
getFrames
();
uint32_t
getWidth
();
uint32_t
getHeight
();
double
getFps
();
protected
:
bool
parseAviWithFrameList
(
MjpegInputStream
&
in_str
,
frame_list
&
in_frame_list
);
void
skipJunk
(
RiffChunk
&
chunk
,
MjpegInputStream
&
in_str
);
void
skipJunk
(
RiffList
&
list
,
MjpegInputStream
&
in_str
);
bool
parseHdrlList
(
MjpegInputStream
&
in_str
);
bool
parseIndex
(
MjpegInputStream
&
in_str
,
uint32_t
index_size
,
frame_list
&
in_frame_list
);
bool
parseMovi
(
MjpegInputStream
&
in_str
,
frame_list
&
in_frame_list
);
bool
parseStrl
(
MjpegInputStream
&
in_str
,
uint8_t
stream_id
);
bool
parseInfo
(
MjpegInputStream
&
in_str
);
void
printError
(
MjpegInputStream
&
in_str
,
RiffList
&
list
,
uint32_t
expected_fourcc
);
void
printError
(
MjpegInputStream
&
in_str
,
RiffChunk
&
chunk
,
uint32_t
expected_fourcc
);
uint32_t
m_stream_id
;
uint64_t
m_movi_start
;
uint64_t
m_movi_end
;
frame_list
m_frame_list
;
uint32_t
m_width
;
uint32_t
m_height
;
double
m_fps
;
bool
m_is_indx_present
;
};
AviMjpegStream
::
AviMjpegStream
()
:
m_stream_id
(
0
),
m_movi_start
(
0
),
m_movi_end
(
0
),
m_width
(
0
),
m_height
(
0
),
m_fps
(
0
),
m_is_indx_present
(
false
)
{
}
size_t
AviMjpegStream
::
getFramesCount
()
{
return
m_frame_list
.
size
();
}
frame_list
&
AviMjpegStream
::
getFrames
()
{
return
m_frame_list
;
}
uint32_t
AviMjpegStream
::
getWidth
()
{
return
m_width
;
}
uint32_t
AviMjpegStream
::
getHeight
()
{
return
m_height
;
}
double
AviMjpegStream
::
getFps
()
{
return
m_fps
;
}
void
AviMjpegStream
::
printError
(
MjpegInputStream
&
in_str
,
RiffList
&
list
,
uint32_t
expected_fourcc
)
{
if
(
!
in_str
)
{
fprintf
(
stderr
,
"Unexpected end of file while searching for %s list
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
());
}
else
if
(
list
.
m_riff_or_list_cc
!=
LIST_CC
)
{
fprintf
(
stderr
,
"Unexpected element. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
LIST_CC
).
c_str
(),
fourccToString
(
list
.
m_riff_or_list_cc
).
c_str
());
}
else
{
fprintf
(
stderr
,
"Unexpected list type. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
(),
fourccToString
(
list
.
m_list_type_cc
).
c_str
());
}
}
void
AviMjpegStream
::
printError
(
MjpegInputStream
&
in_str
,
RiffChunk
&
chunk
,
uint32_t
expected_fourcc
)
{
if
(
!
in_str
)
{
fprintf
(
stderr
,
"Unexpected end of file while searching for %s chunk
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
());
}
else
{
fprintf
(
stderr
,
"Unexpected element. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
(),
fourccToString
(
chunk
.
m_four_cc
).
c_str
());
}
}
bool
AviMjpegStream
::
parseMovi
(
MjpegInputStream
&
,
frame_list
&
)
{
//not implemented
return
true
;
}
bool
AviMjpegStream
::
parseInfo
(
MjpegInputStream
&
)
{
//not implemented
return
true
;
}
bool
AviMjpegStream
::
parseIndex
(
MjpegInputStream
&
in_str
,
uint32_t
index_size
,
frame_list
&
in_frame_list
)
{
uint64_t
index_end
=
in_str
.
tellg
();
index_end
+=
index_size
;
bool
result
=
false
;
while
(
in_str
&&
(
in_str
.
tellg
()
<
index_end
))
{
AviIndex
idx1
;
in_str
>>
idx1
;
if
(
idx1
.
ckid
==
m_stream_id
)
{
uint64_t
absolute_pos
=
m_movi_start
+
idx1
.
dwChunkOffset
;
if
(
absolute_pos
<
m_movi_end
)
{
in_frame_list
.
push_back
(
std
::
make_pair
(
absolute_pos
,
idx1
.
dwChunkLength
));
}
else
{
//unsupported case
fprintf
(
stderr
,
"Frame offset points outside movi section.
\n
"
);
}
}
result
=
true
;
}
return
result
;
}
bool
AviMjpegStream
::
parseStrl
(
MjpegInputStream
&
in_str
,
uint8_t
stream_id
)
{
RiffChunk
strh
;
in_str
>>
strh
;
if
(
in_str
&&
strh
.
m_four_cc
==
STRH_CC
)
{
uint64_t
next_strl_list
=
in_str
.
tellg
();
next_strl_list
+=
strh
.
m_size
;
AviStreamHeader
strm_hdr
;
in_str
>>
strm_hdr
;
if
(
strm_hdr
.
fccType
==
VIDS_CC
&&
strm_hdr
.
fccHandler
==
MJPG_CC
)
{
uint8_t
first_digit
=
(
stream_id
/
10
)
+
'0'
;
uint8_t
second_digit
=
(
stream_id
%
10
)
+
'0'
;
if
(
m_stream_id
==
0
)
{
m_stream_id
=
CV_FOURCC
(
first_digit
,
second_digit
,
'd'
,
'c'
);
m_fps
=
double
(
strm_hdr
.
dwRate
)
/
strm_hdr
.
dwScale
;
}
else
{
//second mjpeg video stream found which is not supported
fprintf
(
stderr
,
"More than one video stream found within AVI/AVIX list. Stream %c%cdc would be ignored
\n
"
,
first_digit
,
second_digit
);
}
return
true
;
}
}
return
false
;
}
void
AviMjpegStream
::
skipJunk
(
RiffChunk
&
chunk
,
MjpegInputStream
&
in_str
)
{
if
(
chunk
.
m_four_cc
==
JUNK_CC
)
{
in_str
.
seekg
(
in_str
.
tellg
()
+
chunk
.
m_size
);
in_str
>>
chunk
;
}
}
void
AviMjpegStream
::
skipJunk
(
RiffList
&
list
,
MjpegInputStream
&
in_str
)
{
if
(
list
.
m_riff_or_list_cc
==
JUNK_CC
)
{
//JUNK chunk is 4 bytes less than LIST
in_str
.
seekg
(
in_str
.
tellg
()
+
list
.
m_size
-
4
);
in_str
>>
list
;
}
}
bool
AviMjpegStream
::
parseHdrlList
(
MjpegInputStream
&
in_str
)
{
bool
result
=
false
;
RiffChunk
avih
;
in_str
>>
avih
;
if
(
in_str
&&
avih
.
m_four_cc
==
AVIH_CC
)
{
uint64_t
next_strl_list
=
in_str
.
tellg
();
next_strl_list
+=
avih
.
m_size
;
AviMainHeader
avi_hdr
;
in_str
>>
avi_hdr
;
if
(
in_str
)
{
m_is_indx_present
=
((
avi_hdr
.
dwFlags
&
0x10
)
!=
0
);
DWORD
number_of_streams
=
avi_hdr
.
dwStreams
;
CV_Assert
(
number_of_streams
<
0xFF
);
m_width
=
avi_hdr
.
dwWidth
;
m_height
=
avi_hdr
.
dwHeight
;
//the number of strl lists must be equal to number of streams specified in main avi header
for
(
DWORD
i
=
0
;
i
<
number_of_streams
;
++
i
)
{
in_str
.
seekg
(
next_strl_list
);
RiffList
strl_list
;
in_str
>>
strl_list
;
if
(
in_str
&&
strl_list
.
m_riff_or_list_cc
==
LIST_CC
&&
strl_list
.
m_list_type_cc
==
STRL_CC
)
{
next_strl_list
=
in_str
.
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_strl_list
+=
(
strl_list
.
m_size
-
4
);
result
=
parseStrl
(
in_str
,
(
uint8_t
)
i
);
}
else
{
printError
(
in_str
,
strl_list
,
STRL_CC
);
}
}
}
}
else
{
printError
(
in_str
,
avih
,
AVIH_CC
);
}
return
result
;
}
bool
AviMjpegStream
::
parseAviWithFrameList
(
MjpegInputStream
&
in_str
,
frame_list
&
in_frame_list
)
{
RiffList
hdrl_list
;
in_str
>>
hdrl_list
;
if
(
in_str
&&
hdrl_list
.
m_riff_or_list_cc
==
LIST_CC
&&
hdrl_list
.
m_list_type_cc
==
HDRL_CC
)
{
uint64_t
next_list
=
in_str
.
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_list
+=
(
hdrl_list
.
m_size
-
4
);
//parseHdrlList sets m_is_indx_present flag which would be used later
if
(
parseHdrlList
(
in_str
))
{
in_str
.
seekg
(
next_list
);
RiffList
some_list
;
in_str
>>
some_list
;
//an optional section INFO
if
(
in_str
&&
some_list
.
m_riff_or_list_cc
==
LIST_CC
&&
some_list
.
m_list_type_cc
==
INFO_CC
)
{
next_list
=
in_str
.
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_list
+=
(
some_list
.
m_size
-
4
);
parseInfo
(
in_str
);
in_str
.
seekg
(
next_list
);
in_str
>>
some_list
;
}
//an optional section JUNK
skipJunk
(
some_list
,
in_str
);
//we are expecting to find here movi list. Must present in avi
if
(
in_str
&&
some_list
.
m_riff_or_list_cc
==
LIST_CC
&&
some_list
.
m_list_type_cc
==
MOVI_CC
)
{
bool
is_index_found
=
false
;
m_movi_start
=
in_str
.
tellg
();
m_movi_start
-=
4
;
m_movi_end
=
m_movi_start
+
some_list
.
m_size
;
//if m_is_indx_present is set to true we should find index
if
(
m_is_indx_present
)
{
//we are expecting to find index section after movi list
uint32_t
indx_pos
=
(
uint32_t
)
m_movi_start
+
4
;
indx_pos
+=
(
some_list
.
m_size
-
4
);
in_str
.
seekg
(
indx_pos
);
RiffChunk
index_chunk
;
in_str
>>
index_chunk
;
if
(
in_str
&&
index_chunk
.
m_four_cc
==
IDX1_CC
)
{
is_index_found
=
parseIndex
(
in_str
,
index_chunk
.
m_size
,
in_frame_list
);
//we are not going anywhere else
}
else
{
printError
(
in_str
,
index_chunk
,
IDX1_CC
);
}
}
//index not present or we were not able to find it
//parsing movi list
if
(
!
is_index_found
)
{
//not implemented
parseMovi
(
in_str
,
in_frame_list
);
fprintf
(
stderr
,
"Failed to parse avi: index was not found
\n
"
);
//we are not going anywhere else
}
}
else
{
printError
(
in_str
,
some_list
,
MOVI_CC
);
}
}
}
else
{
printError
(
in_str
,
hdrl_list
,
HDRL_CC
);
}
return
in_frame_list
.
size
()
>
0
;
}
bool
AviMjpegStream
::
parseAvi
(
MjpegInputStream
&
in_str
,
frame_list
&
in_frame_list
)
{
return
parseAviWithFrameList
(
in_str
,
in_frame_list
);
}
bool
AviMjpegStream
::
parseAvi
(
MjpegInputStream
&
in_str
)
{
return
parseAviWithFrameList
(
in_str
,
m_frame_list
);
}
class
MotionJpegCapture
:
public
IVideoCapture
{
public
:
...
...
@@ -702,12 +61,9 @@ public:
void
close
();
protected
:
bool
parseRiff
(
MjpegInputStream
&
in_str
);
inline
uint64_t
getFramePos
()
const
;
std
::
vector
<
char
>
readFrame
(
frame_iterator
it
);
MjpegInputStream
m_file_stream
;
Ptr
<
AVIReadContainer
>
m_avi_container
;
bool
m_is_first_frame
;
frame_list
m_mjpeg_frames
;
...
...
@@ -779,23 +135,6 @@ double MotionJpegCapture::getProperty(int property) const
}
}
std
::
vector
<
char
>
MotionJpegCapture
::
readFrame
(
frame_iterator
it
)
{
m_file_stream
.
seekg
(
it
->
first
);
RiffChunk
chunk
;
m_file_stream
>>
chunk
;
std
::
vector
<
char
>
result
;
result
.
reserve
(
chunk
.
m_size
);
result
.
resize
(
chunk
.
m_size
);
m_file_stream
.
read
(
&
(
result
[
0
]),
chunk
.
m_size
);
// result.data() failed with MSVS2008
return
result
;
}
bool
MotionJpegCapture
::
grabFrame
()
{
if
(
isOpened
())
...
...
@@ -818,7 +157,7 @@ bool MotionJpegCapture::retrieveFrame(int, OutputArray output_frame)
{
if
(
m_frame_iterator
!=
m_mjpeg_frames
.
end
())
{
std
::
vector
<
char
>
data
=
readFrame
(
m_frame_iterator
);
std
::
vector
<
char
>
data
=
m_avi_container
->
readFrame
(
m_frame_iterator
);
if
(
data
.
size
())
{
...
...
@@ -840,6 +179,8 @@ MotionJpegCapture::~MotionJpegCapture()
MotionJpegCapture
::
MotionJpegCapture
(
const
String
&
filename
)
{
m_avi_container
=
makePtr
<
AVIReadContainer
>
();
m_avi_container
->
initStream
(
filename
);
open
(
filename
);
}
...
...
@@ -850,7 +191,7 @@ bool MotionJpegCapture::isOpened() const
void
MotionJpegCapture
::
close
()
{
m_
file_stream
.
close
();
m_
avi_container
->
close
();
m_frame_iterator
=
m_mjpeg_frames
.
end
();
}
...
...
@@ -858,56 +199,23 @@ bool MotionJpegCapture::open(const String& filename)
{
close
();
m_file_stream
.
open
(
filename
);
m_avi_container
=
makePtr
<
AVIReadContainer
>
();
m_avi_container
->
initStream
(
filename
);
m_frame_iterator
=
m_mjpeg_frames
.
end
();
m_is_first_frame
=
true
;
if
(
!
parseRiff
(
m_file_stream
))
if
(
!
m_avi_container
->
parseRiff
(
m_mjpeg_frames
))
{
close
();
}
return
isOpened
();
}
bool
MotionJpegCapture
::
parseRiff
(
MjpegInputStream
&
in_str
)
{
bool
result
=
false
;
while
(
in_str
)
}
else
{
RiffList
riff_list
;
in_str
>>
riff_list
;
if
(
in_str
&&
riff_list
.
m_riff_or_list_cc
==
RIFF_CC
&&
((
riff_list
.
m_list_type_cc
==
AVI_CC
)
|
(
riff_list
.
m_list_type_cc
==
AVIX_CC
))
)
{
uint64_t
next_riff
=
in_str
.
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_riff
+=
(
riff_list
.
m_size
-
4
);
AviMjpegStream
mjpeg_video_stream
;
bool
is_parsed
=
mjpeg_video_stream
.
parseAvi
(
in_str
,
m_mjpeg_frames
);
result
=
result
||
is_parsed
;
if
(
is_parsed
)
{
m_frame_width
=
mjpeg_video_stream
.
getWidth
();
m_frame_height
=
mjpeg_video_stream
.
getHeight
();
m_fps
=
mjpeg_video_stream
.
getFps
();
}
in_str
.
seekg
(
next_riff
);
}
else
{
break
;
}
m_frame_width
=
m_avi_container
->
getWidth
();
m_frame_height
=
m_avi_container
->
getHeight
();
m_fps
=
m_avi_container
->
getFps
();
}
return
result
;
return
isOpened
()
;
}
Ptr
<
IVideoCapture
>
createMotionJpegCapture
(
const
String
&
filename
)
...
...
modules/videoio/src/cap_mjpeg_encoder.cpp
View file @
30b33a44
...
...
@@ -40,8 +40,12 @@
//M*/
#include "precomp.hpp"
#include "opencv2/videoio/container_avi.private.hpp"
#include <vector>
#include <deque>
#include <iostream>
#include <cstdlib>
#if CV_NEON
#define WITH_NEON
...
...
@@ -49,22 +53,6 @@
namespace
cv
{
namespace
mjpeg
{
enum
{
COLORSPACE_GRAY
=
0
,
COLORSPACE_RGBA
=
1
,
COLORSPACE_BGR
=
2
,
COLORSPACE_YUV444P
=
3
};
#define fourCC(a,b,c,d) ((int)((uchar(d)<<24) | (uchar(c)<<16) | (uchar(b)<<8) | uchar(a)))
static
const
int
AVIH_STRH_SIZE
=
56
;
static
const
int
STRF_SIZE
=
40
;
static
const
int
AVI_DWFLAG
=
0x00000910
;
static
const
int
AVI_DWSCALE
=
1
;
static
const
int
AVI_DWQUALITY
=
-
1
;
static
const
int
JUNK_SEEK
=
4096
;
static
const
int
AVIIF_KEYFRAME
=
0x10
;
static
const
int
MAX_BYTES_PER_SEC
=
99999999
;
static
const
int
SUG_BUFFER_SIZE
=
1048576
;
static
const
unsigned
bit_mask
[]
=
{
...
...
@@ -79,279 +67,84 @@ static const unsigned bit_mask[] =
0x1FFFFFFF
,
0x3FFFFFFF
,
0x7FFFFFFF
,
0xFFFFFFFF
};
class
BitStream
{
public
:
enum
{
DEFAULT_BLOCK_SIZE
=
(
1
<<
15
),
huff_val_shift
=
20
,
huff_code_mask
=
(
1
<<
huff_val_shift
)
-
1
};
BitStream
()
{
m_buf
.
resize
(
DEFAULT_BLOCK_SIZE
+
1024
);
m_start
=
&
m_buf
[
0
];
m_end
=
m_start
+
DEFAULT_BLOCK_SIZE
;
m_is_opened
=
false
;
m_f
=
0
;
m_current
=
0
;
m_pos
=
0
;
}
static
const
uchar
huff_val_shift
=
20
;
static
const
int
huff_code_mask
=
(
1
<<
huff_val_shift
)
-
1
;
~
BitStream
()
{
close
();
}
bool
open
(
const
String
&
filename
)
{
close
();
m_f
=
fopen
(
filename
.
c_str
(),
"wb"
);
if
(
!
m_f
)
return
false
;
m_current
=
m_start
;
m_pos
=
0
;
return
true
;
}
bool
isOpened
()
const
{
return
m_f
!=
0
;
}
static
bool
createEncodeHuffmanTable
(
const
int
*
src
,
unsigned
*
table
,
int
max_size
)
{
int
i
,
k
;
int
min_val
=
INT_MAX
,
max_val
=
INT_MIN
;
int
size
;
void
close
()
/* calc min and max values in the table */
for
(
i
=
1
,
k
=
1
;
src
[
k
]
>=
0
;
i
++
)
{
writeBlock
();
if
(
m_f
)
fclose
(
m_f
);
m_f
=
0
;
}
int
code_count
=
src
[
k
++
];
void
writeBlock
()
{
size_t
wsz0
=
m_current
-
m_start
;
if
(
wsz0
>
0
&&
m_f
)
for
(
code_count
+=
k
;
k
<
code_count
;
k
++
)
{
size_t
wsz
=
fwrite
(
m_start
,
1
,
wsz0
,
m_f
);
CV_Assert
(
wsz
==
wsz0
);
int
val
=
src
[
k
]
>>
huff_val_shift
;
if
(
val
<
min_val
)
min_val
=
val
;
if
(
val
>
max_val
)
max_val
=
val
;
}
m_pos
+=
wsz0
;
m_current
=
m_start
;
}
size_t
getPos
()
const
{
return
(
size_t
)(
m_current
-
m_start
)
+
m_pos
;
}
size
=
max_val
-
min_val
+
3
;
void
putByte
(
int
val
)
if
(
size
>
max_size
)
{
*
m_current
++
=
(
uchar
)
val
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
putBytes
(
const
uchar
*
buf
,
int
count
)
{
uchar
*
data
=
(
uchar
*
)
buf
;
CV_Assert
(
m_f
&&
data
&&
m_current
&&
count
>=
0
);
if
(
m_current
>=
m_end
)
writeBlock
();
while
(
count
)
{
int
l
=
(
int
)(
m_end
-
m_current
);
if
(
l
>
count
)
l
=
count
;
if
(
l
>
0
)
{
memcpy
(
m_current
,
data
,
l
);
m_current
+=
l
;
data
+=
l
;
count
-=
l
;
}
if
(
m_current
>=
m_end
)
writeBlock
();
}
CV_Error
(
CV_StsOutOfRange
,
"too big maximum Huffman code size"
);
return
false
;
}
void
putShort
(
int
val
)
{
m_current
[
0
]
=
(
uchar
)
val
;
m_current
[
1
]
=
(
uchar
)(
val
>>
8
);
m_current
+=
2
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
memset
(
table
,
0
,
size
*
sizeof
(
table
[
0
]));
void
putInt
(
int
val
)
{
m_current
[
0
]
=
(
uchar
)
val
;
m_current
[
1
]
=
(
uchar
)(
val
>>
8
);
m_current
[
2
]
=
(
uchar
)(
val
>>
16
);
m_current
[
3
]
=
(
uchar
)(
val
>>
24
);
m_current
+=
4
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
table
[
0
]
=
min_val
;
table
[
1
]
=
size
-
2
;
void
jputShort
(
int
val
)
for
(
i
=
1
,
k
=
1
;
src
[
k
]
>=
0
;
i
++
)
{
m_current
[
0
]
=
(
uchar
)(
val
>>
8
);
m_current
[
1
]
=
(
uchar
)
val
;
m_current
+=
2
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
int
code_count
=
src
[
k
++
];
void
patchInt
(
int
val
,
size_t
pos
)
{
if
(
pos
>=
m_pos
)
{
ptrdiff_t
delta
=
pos
-
m_pos
;
CV_Assert
(
delta
<
m_current
-
m_start
);
m_start
[
delta
]
=
(
uchar
)
val
;
m_start
[
delta
+
1
]
=
(
uchar
)(
val
>>
8
);
m_start
[
delta
+
2
]
=
(
uchar
)(
val
>>
16
);
m_start
[
delta
+
3
]
=
(
uchar
)(
val
>>
24
);
}
else
for
(
code_count
+=
k
;
k
<
code_count
;
k
++
)
{
long
fpos
=
ftell
(
m_f
);
fseek
(
m_f
,
(
long
)
pos
,
SEEK_SET
);
uchar
buf
[]
=
{
(
uchar
)
val
,
(
uchar
)(
val
>>
8
),
(
uchar
)(
val
>>
16
),
(
uchar
)(
val
>>
24
)
};
fwrite
(
buf
,
1
,
4
,
m_f
);
fseek
(
m_f
,
fpos
,
SEEK_SET
);
}
}
int
val
=
src
[
k
]
>>
huff_val_shift
;
int
code
=
src
[
k
]
&
huff_code_mask
;
void
jput
(
unsigned
currval
)
{
uchar
v
;
uchar
*
ptr
=
m_current
;
v
=
(
uchar
)(
currval
>>
24
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)(
currval
>>
16
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)(
currval
>>
8
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)
currval
;
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
m_current
=
ptr
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
jflush
(
unsigned
currval
,
int
bitIdx
)
{
uchar
v
;
uchar
*
ptr
=
m_current
;
currval
|=
(
1
<<
bitIdx
)
-
1
;
while
(
bitIdx
<
32
)
{
v
=
(
uchar
)(
currval
>>
24
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
currval
<<=
8
;
bitIdx
+=
8
;
table
[
val
-
min_val
+
2
]
=
(
code
<<
8
)
|
i
;
}
m_current
=
ptr
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
static
bool
createEncodeHuffmanTable
(
const
int
*
src
,
unsigned
*
table
,
int
max_size
)
{
int
i
,
k
;
int
min_val
=
INT_MAX
,
max_val
=
INT_MIN
;
int
size
;
/* calc min and max values in the table */
for
(
i
=
1
,
k
=
1
;
src
[
k
]
>=
0
;
i
++
)
{
int
code_count
=
src
[
k
++
];
for
(
code_count
+=
k
;
k
<
code_count
;
k
++
)
{
int
val
=
src
[
k
]
>>
huff_val_shift
;
if
(
val
<
min_val
)
min_val
=
val
;
if
(
val
>
max_val
)
max_val
=
val
;
}
}
size
=
max_val
-
min_val
+
3
;
if
(
size
>
max_size
)
{
CV_Error
(
CV_StsOutOfRange
,
"too big maximum Huffman code size"
);
return
false
;
}
memset
(
table
,
0
,
size
*
sizeof
(
table
[
0
]));
table
[
0
]
=
min_val
;
table
[
1
]
=
size
-
2
;
for
(
i
=
1
,
k
=
1
;
src
[
k
]
>=
0
;
i
++
)
{
int
code_count
=
src
[
k
++
];
for
(
code_count
+=
k
;
k
<
code_count
;
k
++
)
{
int
val
=
src
[
k
]
>>
huff_val_shift
;
int
code
=
src
[
k
]
&
huff_code_mask
;
table
[
val
-
min_val
+
2
]
=
(
code
<<
8
)
|
i
;
}
}
return
true
;
}
return
true
;
}
static
int
*
createSourceHuffmanTable
(
const
uchar
*
src
,
int
*
dst
,
static
int
*
createSourceHuffmanTable
(
const
uchar
*
src
,
int
*
dst
,
int
max_bits
,
int
first_bits
)
{
int
i
,
val_idx
,
code
=
0
;
int
*
table
=
dst
;
*
dst
++
=
first_bits
;
for
(
i
=
1
,
val_idx
=
max_bits
;
i
<=
max_bits
;
i
++
)
{
int
i
,
val_idx
,
code
=
0
;
int
*
table
=
ds
t
;
*
dst
++
=
first_bits
;
for
(
i
=
1
,
val_idx
=
max_bits
;
i
<=
max_bits
;
i
++
)
int
code_count
=
src
[
i
-
1
]
;
dst
[
0
]
=
code_coun
t
;
code
<<=
1
;
for
(
i
nt
k
=
0
;
k
<
code_count
;
k
++
)
{
int
code_count
=
src
[
i
-
1
];
dst
[
0
]
=
code_count
;
code
<<=
1
;
for
(
int
k
=
0
;
k
<
code_count
;
k
++
)
{
dst
[
k
+
1
]
=
(
src
[
val_idx
+
k
]
<<
huff_val_shift
)
|
(
code
+
k
);
}
code
+=
code_count
;
dst
+=
code_count
+
1
;
val_idx
+=
code_count
;
dst
[
k
+
1
]
=
(
src
[
val_idx
+
k
]
<<
huff_val_shift
)
|
(
code
+
k
);
}
dst
[
0
]
=
-
1
;
return
table
;
code
+=
code_count
;
dst
+=
code_count
+
1
;
val_idx
+=
code_count
;
}
dst
[
0
]
=
-
1
;
return
table
;
}
protected
:
std
::
vector
<
uchar
>
m_buf
;
uchar
*
m_start
;
uchar
*
m_end
;
uchar
*
m_current
;
size_t
m_pos
;
bool
m_is_opened
;
FILE
*
m_f
;
};
namespace
mjpeg
{
class
mjpeg_buffer
{
...
...
@@ -593,11 +386,6 @@ public:
{
rawstream
=
false
;
nstripes
=
-
1
;
height
=
0
;
width
=
0
;
moviPointer
=
0
;
channels
=
0
;
outfps
=
0
;
quality
=
0
;
}
...
...
@@ -611,20 +399,15 @@ public:
void
close
()
{
if
(
!
strm
.
isOpened
()
)
if
(
!
container
.
isOpenedStream
()
)
return
;
if
(
!
frameOffset
.
empty
()
&&
!
rawstream
)
if
(
!
container
.
isEmptyFrameOffset
()
&&
!
rawstream
)
{
endWriteChunk
();
// end LIST 'movi'
writeIndex
(
);
finishWriteAVI
();
container
.
endWriteChunk
();
// end LIST 'movi'
container
.
writeIndex
(
0
,
dc
);
container
.
finishWriteAVI
();
}
strm
.
close
();
frameOffset
.
clear
();
frameSize
.
clear
();
AVIChunkSizeIndex
.
clear
();
frameNumIndexes
.
clear
();
}
bool
open
(
const
String
&
filename
,
double
fps
,
Size
size
,
bool
iscolor
)
...
...
@@ -639,222 +422,74 @@ public:
if
(
strcmp
(
ext
,
".avi"
)
!=
0
&&
strcmp
(
ext
,
".AVI"
)
!=
0
&&
strcmp
(
ext
,
".Avi"
)
!=
0
)
return
false
;
bool
ok
=
strm
.
open
(
filename
);
if
(
!
ok
)
if
(
!
container
.
initContainer
(
filename
,
fps
,
size
,
iscolor
)
)
return
false
;
CV_Assert
(
fps
>=
1
);
outfps
=
cvRound
(
fps
);
width
=
size
.
width
;
height
=
size
.
height
;
quality
=
75
;
rawstream
=
false
;
channels
=
iscolor
?
3
:
1
;
if
(
!
rawstream
)
{
startWriteAVI
();
writeStreamHeader
(
);
container
.
startWriteAVI
(
1
);
// count stream
container
.
writeStreamHeader
(
MJPEG
);
}
//printf("motion jpeg stream %s has been successfully opened\n", filename.c_str());
return
true
;
}
bool
isOpened
()
const
{
return
strm
.
isOpened
();
}
void
startWriteAVI
()
{
startWriteChunk
(
fourCC
(
'R'
,
'I'
,
'F'
,
'F'
));
strm
.
putInt
(
fourCC
(
'A'
,
'V'
,
'I'
,
' '
));
startWriteChunk
(
fourCC
(
'L'
,
'I'
,
'S'
,
'T'
));
strm
.
putInt
(
fourCC
(
'h'
,
'd'
,
'r'
,
'l'
));
strm
.
putInt
(
fourCC
(
'a'
,
'v'
,
'i'
,
'h'
));
strm
.
putInt
(
AVIH_STRH_SIZE
);
strm
.
putInt
(
cvRound
(
1e6
/
outfps
));
strm
.
putInt
(
MAX_BYTES_PER_SEC
);
strm
.
putInt
(
0
);
strm
.
putInt
(
AVI_DWFLAG
);
frameNumIndexes
.
push_back
(
strm
.
getPos
());
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
1
);
// number of streams
strm
.
putInt
(
SUG_BUFFER_SIZE
);
strm
.
putInt
(
width
);
strm
.
putInt
(
height
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
}
void
writeStreamHeader
()
{
// strh
startWriteChunk
(
fourCC
(
'L'
,
'I'
,
'S'
,
'T'
));
strm
.
putInt
(
fourCC
(
's'
,
't'
,
'r'
,
'l'
));
strm
.
putInt
(
fourCC
(
's'
,
't'
,
'r'
,
'h'
));
strm
.
putInt
(
AVIH_STRH_SIZE
);
strm
.
putInt
(
fourCC
(
'v'
,
'i'
,
'd'
,
's'
));
strm
.
putInt
(
fourCC
(
'M'
,
'J'
,
'P'
,
'G'
));
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
AVI_DWSCALE
);
strm
.
putInt
(
outfps
);
strm
.
putInt
(
0
);
frameNumIndexes
.
push_back
(
strm
.
getPos
());
strm
.
putInt
(
0
);
strm
.
putInt
(
SUG_BUFFER_SIZE
);
strm
.
putInt
(
AVI_DWQUALITY
);
strm
.
putInt
(
0
);
strm
.
putShort
(
0
);
strm
.
putShort
(
0
);
strm
.
putShort
(
width
);
strm
.
putShort
(
height
);
// strf (use the BITMAPINFOHEADER for video)
startWriteChunk
(
fourCC
(
's'
,
't'
,
'r'
,
'f'
));
strm
.
putInt
(
STRF_SIZE
);
strm
.
putInt
(
width
);
strm
.
putInt
(
height
);
strm
.
putShort
(
1
);
// planes (1 means interleaved data (after decompression))
strm
.
putShort
(
8
*
channels
);
// bits per pixel
strm
.
putInt
(
fourCC
(
'M'
,
'J'
,
'P'
,
'G'
));
strm
.
putInt
(
width
*
height
*
channels
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
// Must be indx chunk
endWriteChunk
();
// end strf
endWriteChunk
();
// end strl
// odml
startWriteChunk
(
fourCC
(
'L'
,
'I'
,
'S'
,
'T'
));
strm
.
putInt
(
fourCC
(
'o'
,
'd'
,
'm'
,
'l'
));
startWriteChunk
(
fourCC
(
'd'
,
'm'
,
'l'
,
'h'
));
frameNumIndexes
.
push_back
(
strm
.
getPos
());
strm
.
putInt
(
0
);
strm
.
putInt
(
0
);
endWriteChunk
();
// end dmlh
endWriteChunk
();
// end odml
endWriteChunk
();
// end hdrl
// JUNK
startWriteChunk
(
fourCC
(
'J'
,
'U'
,
'N'
,
'K'
));
size_t
pos
=
strm
.
getPos
();
for
(
;
pos
<
(
size_t
)
JUNK_SEEK
;
pos
+=
4
)
strm
.
putInt
(
0
);
endWriteChunk
();
// end JUNK
// movi
startWriteChunk
(
fourCC
(
'L'
,
'I'
,
'S'
,
'T'
));
moviPointer
=
strm
.
getPos
();
strm
.
putInt
(
fourCC
(
'm'
,
'o'
,
'v'
,
'i'
));
}
void
startWriteChunk
(
int
fourcc
)
{
CV_Assert
(
fourcc
!=
0
);
strm
.
putInt
(
fourcc
);
AVIChunkSizeIndex
.
push_back
(
strm
.
getPos
());
strm
.
putInt
(
0
);
}
void
endWriteChunk
()
{
if
(
!
AVIChunkSizeIndex
.
empty
()
)
{
size_t
currpos
=
strm
.
getPos
();
size_t
pospos
=
AVIChunkSizeIndex
.
back
();
AVIChunkSizeIndex
.
pop_back
();
int
chunksz
=
(
int
)(
currpos
-
(
pospos
+
4
));
strm
.
patchInt
(
chunksz
,
pospos
);
}
}
void
writeIndex
()
{
// old style AVI index. Must be Open-DML index
startWriteChunk
(
fourCC
(
'i'
,
'd'
,
'x'
,
'1'
));
int
nframes
=
(
int
)
frameOffset
.
size
();
for
(
int
i
=
0
;
i
<
nframes
;
i
++
)
{
strm
.
putInt
(
fourCC
(
'0'
,
'0'
,
'd'
,
'c'
));
strm
.
putInt
(
AVIIF_KEYFRAME
);
strm
.
putInt
((
int
)
frameOffset
[
i
]);
strm
.
putInt
((
int
)
frameSize
[
i
]);
}
endWriteChunk
();
// End idx1
}
void
finishWriteAVI
()
{
int
nframes
=
(
int
)
frameOffset
.
size
();
// Record frames numbers to AVI Header
while
(
!
frameNumIndexes
.
empty
())
{
size_t
ppos
=
frameNumIndexes
.
back
();
frameNumIndexes
.
pop_back
();
strm
.
patchInt
(
nframes
,
ppos
);
}
endWriteChunk
();
// end RIFF
}
bool
isOpened
()
const
{
return
container
.
isOpenedStream
();
}
void
write
(
InputArray
_img
)
{
Mat
img
=
_img
.
getMat
();
size_t
chunkPointer
=
strm
.
get
Pos
();
size_t
chunkPointer
=
container
.
getStream
Pos
();
int
input_channels
=
img
.
channels
();
int
colorspace
=
-
1
;
int
imgWidth
=
img
.
cols
;
int
frameWidth
=
container
.
getWidth
();
int
imgHeight
=
img
.
rows
;
int
frameHeight
=
container
.
getHeight
();
int
channels
=
container
.
getChannels
();
if
(
input_channels
==
1
&&
channels
==
1
)
{
CV_Assert
(
img
.
cols
==
width
&&
img
.
rows
==
h
eight
);
CV_Assert
(
img
Width
==
frameWidth
&&
imgHeight
==
frameH
eight
);
colorspace
=
COLORSPACE_GRAY
;
}
else
if
(
input_channels
==
4
)
{
CV_Assert
(
img
.
cols
==
width
&&
img
.
rows
==
h
eight
&&
channels
==
3
);
CV_Assert
(
img
Width
==
frameWidth
&&
imgHeight
==
frameH
eight
&&
channels
==
3
);
colorspace
=
COLORSPACE_RGBA
;
}
else
if
(
input_channels
==
3
)
{
CV_Assert
(
img
.
cols
==
width
&&
img
.
rows
==
h
eight
&&
channels
==
3
);
CV_Assert
(
img
Width
==
frameWidth
&&
imgHeight
==
frameH
eight
&&
channels
==
3
);
colorspace
=
COLORSPACE_BGR
;
}
else
if
(
input_channels
==
1
&&
channels
==
3
)
{
CV_Assert
(
img
.
cols
==
width
&&
img
.
rows
==
h
eight
*
3
);
CV_Assert
(
img
Width
==
frameWidth
&&
imgHeight
==
frameH
eight
*
3
);
colorspace
=
COLORSPACE_YUV444P
;
}
else
CV_Error
(
CV_StsBadArg
,
"Invalid combination of specified video colorspace and the input image colorspace"
);
if
(
!
rawstream
)
startWriteChunk
(
fourCC
(
'0'
,
'0'
,
'd'
,
'c'
));
if
(
!
rawstream
)
{
int
avi_index
=
container
.
getAVIIndex
(
0
,
dc
);
container
.
startWriteChunk
(
avi_index
);
}
writeFrameData
(
img
.
data
,
(
int
)
img
.
step
,
colorspace
,
input_channels
);
if
(
!
rawstream
)
{
frameOffset
.
push_back
(
chunkPointer
-
moviPointer
);
frameSize
.
push_back
(
strm
.
getPos
()
-
chunkPointer
-
8
);
// Size excludes '00dc' and size field
endWriteChunk
();
// end '00dc'
size_t
tempChunkPointer
=
container
.
getStreamPos
();
size_t
moviPointer
=
container
.
getMoviPointer
();
container
.
pushFrameOffset
(
chunkPointer
-
moviPointer
);
container
.
pushFrameSize
(
tempChunkPointer
-
chunkPointer
-
8
);
// Size excludes '00dc' and size field
container
.
endWriteChunk
();
// end '00dc'
}
}
...
...
@@ -863,7 +498,10 @@ public:
if
(
propId
==
VIDEOWRITER_PROP_QUALITY
)
return
quality
;
if
(
propId
==
VIDEOWRITER_PROP_FRAMEBYTES
)
return
frameSize
.
empty
()
?
0.
:
(
double
)
frameSize
.
back
();
{
bool
isEmpty
=
container
.
isEmptyFrameSize
();
return
isEmpty
?
0.
:
container
.
atFrameSize
(
container
.
countFrameSize
()
-
1
);
}
if
(
propId
==
VIDEOWRITER_PROP_NSTRIPES
)
return
nstripes
;
return
0.
;
...
...
@@ -889,16 +527,12 @@ public:
void
writeFrameData
(
const
uchar
*
data
,
int
step
,
int
colorspace
,
int
input_channels
);
protected
:
int
outfps
;
int
width
,
height
,
channels
;
double
quality
;
size_t
moviPointer
;
std
::
vector
<
size_t
>
frameOffset
,
frameSize
,
AVIChunkSizeIndex
,
frameNumIndexes
;
bool
rawstream
;
mjpeg_buffer_keeper
buffers_list
;
double
nstripes
;
BitStream
strm
;
AVIWriteContainer
container
;
};
#define DCT_DESCALE(x, n) (((x) + (((int)1) << ((n) - 1))) >> (n))
...
...
@@ -1758,6 +1392,10 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
}
//double total_dct = 0, total_cvt = 0;
int
width
=
container
.
getWidth
();
int
height
=
container
.
getHeight
();
int
channels
=
container
.
getChannels
();
CV_Assert
(
data
&&
width
>
0
&&
height
>
0
);
// encode the header and tables
...
...
@@ -1784,7 +1422,7 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
double
inv_quality
=
1.
/
_quality
;
// Encode header
strm
.
put
Bytes
(
(
const
uchar
*
)
jpegHeader
,
sizeof
(
jpegHeader
)
-
1
);
container
.
putStream
Bytes
(
(
const
uchar
*
)
jpegHeader
,
sizeof
(
jpegHeader
)
-
1
);
// Encode quantization tables
for
(
i
=
0
;
i
<
(
channels
>
1
?
2
:
1
);
i
++
)
...
...
@@ -1792,9 +1430,9 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
const
uchar
*
qtable
=
i
==
0
?
jpegTableK1_T
:
jpegTableK2_T
;
int
chroma_scale
=
i
>
0
?
luma_count
:
1
;
strm
.
jput
Short
(
0xffdb
);
// DQT marker
strm
.
jput
Short
(
2
+
65
*
1
);
// put single qtable
strm
.
put
Byte
(
0
*
16
+
i
);
// 8-bit table
container
.
jputStream
Short
(
0xffdb
);
// DQT marker
container
.
jputStream
Short
(
2
+
65
*
1
);
// put single qtable
container
.
putStream
Byte
(
0
*
16
+
i
);
// 8-bit table
// put coefficients
for
(
j
=
0
;
j
<
64
;
j
++
)
...
...
@@ -1807,7 +1445,7 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
qval
=
255
;
fdct_qtab
[
i
][
idx
]
=
(
short
)(
cvRound
((
1
<<
(
postshift
+
11
)))
/
(
qval
*
chroma_scale
*
idct_prescale
[
idx
]));
strm
.
put
Byte
(
qval
);
container
.
putStream
Byte
(
qval
);
}
}
...
...
@@ -1820,49 +1458,49 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
int
idx
=
i
>=
2
;
int
tableSize
=
16
+
(
is_ac_tab
?
162
:
12
);
strm
.
jput
Short
(
0xFFC4
);
// DHT marker
strm
.
jput
Short
(
3
+
tableSize
);
// define one huffman table
strm
.
put
Byte
(
is_ac_tab
*
16
+
idx
);
// put DC/AC flag and table index
strm
.
put
Bytes
(
htable
,
tableSize
);
// put table
container
.
jputStream
Short
(
0xFFC4
);
// DHT marker
container
.
jputStream
Short
(
3
+
tableSize
);
// define one huffman table
container
.
putStream
Byte
(
is_ac_tab
*
16
+
idx
);
// put DC/AC flag and table index
container
.
putStream
Bytes
(
htable
,
tableSize
);
// put table
BitStream
::
createEncodeHuffmanTable
(
BitStream
::
createSourceHuffmanTable
(
htable
,
hbuffer
,
16
,
9
),
is_ac_tab
?
huff_ac_tab
[
idx
]
:
huff_dc_tab
[
idx
],
is_ac_tab
?
256
:
16
);
createEncodeHuffmanTable
(
createSourceHuffmanTable
(
htable
,
hbuffer
,
16
,
9
),
is_ac_tab
?
huff_ac_tab
[
idx
]
:
huff_dc_tab
[
idx
],
is_ac_tab
?
256
:
16
);
}
// put frame header
strm
.
jput
Short
(
0xFFC0
);
// SOF0 marker
strm
.
jput
Short
(
8
+
3
*
channels
);
// length of frame header
strm
.
put
Byte
(
8
);
// sample precision
strm
.
jput
Short
(
height
);
strm
.
jput
Short
(
width
);
strm
.
put
Byte
(
channels
);
// number of components
container
.
jputStream
Short
(
0xFFC0
);
// SOF0 marker
container
.
jputStream
Short
(
8
+
3
*
channels
);
// length of frame header
container
.
putStream
Byte
(
8
);
// sample precision
container
.
jputStream
Short
(
height
);
container
.
jputStream
Short
(
width
);
container
.
putStream
Byte
(
channels
);
// number of components
for
(
i
=
0
;
i
<
channels
;
i
++
)
{
strm
.
put
Byte
(
i
+
1
);
// (i+1)-th component id (Y,U or V)
container
.
putStream
Byte
(
i
+
1
);
// (i+1)-th component id (Y,U or V)
if
(
i
==
0
)
strm
.
put
Byte
(
x_scale
*
16
+
y_scale
);
// chroma scale factors
container
.
putStream
Byte
(
x_scale
*
16
+
y_scale
);
// chroma scale factors
else
strm
.
put
Byte
(
1
*
16
+
1
);
strm
.
put
Byte
(
i
>
0
);
// quantization table idx
container
.
putStream
Byte
(
1
*
16
+
1
);
container
.
putStream
Byte
(
i
>
0
);
// quantization table idx
}
// put scan header
strm
.
jput
Short
(
0xFFDA
);
// SOS marker
strm
.
jput
Short
(
6
+
2
*
channels
);
// length of scan header
strm
.
put
Byte
(
channels
);
// number of components in the scan
container
.
jputStream
Short
(
0xFFDA
);
// SOS marker
container
.
jputStream
Short
(
6
+
2
*
channels
);
// length of scan header
container
.
putStream
Byte
(
channels
);
// number of components in the scan
for
(
i
=
0
;
i
<
channels
;
i
++
)
{
strm
.
put
Byte
(
i
+
1
);
// component id
strm
.
put
Byte
(
(
i
>
0
)
*
16
+
(
i
>
0
)
);
// selection of DC & AC tables
container
.
putStream
Byte
(
i
+
1
);
// component id
container
.
putStream
Byte
(
(
i
>
0
)
*
16
+
(
i
>
0
)
);
// selection of DC & AC tables
}
strm
.
jput
Short
(
0
*
256
+
63
);
// start and end of spectral selection - for
container
.
jputStream
Short
(
0
*
256
+
63
);
// start and end of spectral selection - for
// sequential DCT start is 0 and end is 63
strm
.
put
Byte
(
0
);
// successive approximation bit position
container
.
putStream
Byte
(
0
);
// successive approximation bit position
// high & low - (0,0) for sequential DCT
buffers_list
.
reset
();
...
...
@@ -1877,18 +1515,18 @@ void MotionJpegWriter::writeFrameData( const uchar* data, int step, int colorspa
for
(
unsigned
k
=
0
;
k
<
last_data_elem
;
++
k
)
{
strm
.
jput
(
v
[
k
]);
container
.
jputStream
(
v
[
k
]);
}
strm
.
jflush
(
v
[
last_data_elem
],
32
-
buffers_list
.
get_last_bit_len
());
strm
.
jput
Short
(
0xFFD9
);
// EOI marker
container
.
jflushStream
(
v
[
last_data_elem
],
32
-
buffers_list
.
get_last_bit_len
());
container
.
jputStream
Short
(
0xFFD9
);
// EOI marker
/*printf("total dct = %.1fms, total cvt = %.1fms\n",
total_dct*1000./cv::getTickFrequency(),
total_cvt*1000./cv::getTickFrequency());*/
size_t
pos
=
strm
.
get
Pos
();
size_t
pos
=
container
.
getStream
Pos
();
size_t
pos1
=
(
pos
+
3
)
&
~
3
;
for
(
;
pos
<
pos1
;
pos
++
)
strm
.
put
Byte
(
0
);
container
.
putStream
Byte
(
0
);
}
}
...
...
modules/videoio/src/container_avi.cpp
0 → 100644
View file @
30b33a44
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "opencv2/videoio/container_avi.private.hpp"
namespace
cv
{
const
unsigned
int
RIFF_CC
=
CV_FOURCC
(
'R'
,
'I'
,
'F'
,
'F'
);
const
unsigned
int
LIST_CC
=
CV_FOURCC
(
'L'
,
'I'
,
'S'
,
'T'
);
const
unsigned
int
HDRL_CC
=
CV_FOURCC
(
'h'
,
'd'
,
'r'
,
'l'
);
const
unsigned
int
AVIH_CC
=
CV_FOURCC
(
'a'
,
'v'
,
'i'
,
'h'
);
const
unsigned
int
STRL_CC
=
CV_FOURCC
(
's'
,
't'
,
'r'
,
'l'
);
const
unsigned
int
STRH_CC
=
CV_FOURCC
(
's'
,
't'
,
'r'
,
'h'
);
const
unsigned
int
STRF_CC
=
CV_FOURCC
(
's'
,
't'
,
'r'
,
'f'
);
const
unsigned
int
VIDS_CC
=
CV_FOURCC
(
'v'
,
'i'
,
'd'
,
's'
);
const
unsigned
int
MJPG_CC
=
CV_FOURCC
(
'M'
,
'J'
,
'P'
,
'G'
);
const
unsigned
int
MOVI_CC
=
CV_FOURCC
(
'm'
,
'o'
,
'v'
,
'i'
);
const
unsigned
int
IDX1_CC
=
CV_FOURCC
(
'i'
,
'd'
,
'x'
,
'1'
);
const
unsigned
int
AVI_CC
=
CV_FOURCC
(
'A'
,
'V'
,
'I'
,
' '
);
const
unsigned
int
AVIX_CC
=
CV_FOURCC
(
'A'
,
'V'
,
'I'
,
'X'
);
const
unsigned
int
JUNK_CC
=
CV_FOURCC
(
'J'
,
'U'
,
'N'
,
'K'
);
const
unsigned
int
INFO_CC
=
CV_FOURCC
(
'I'
,
'N'
,
'F'
,
'O'
);
const
unsigned
int
ODML_CC
=
CV_FOURCC
(
'o'
,
'd'
,
'm'
,
'l'
);
const
unsigned
int
DMLH_CC
=
CV_FOURCC
(
'd'
,
'm'
,
'l'
,
'h'
);
String
fourccToString
(
unsigned
int
fourcc
);
#ifndef DWORD
typedef
unsigned
int
DWORD
;
#endif
#ifndef WORD
typedef
unsigned
short
int
WORD
;
#endif
#ifndef LONG
typedef
int
LONG
;
#endif
#pragma pack(push, 1)
struct
AviMainHeader
{
DWORD
dwMicroSecPerFrame
;
// The period between video frames
DWORD
dwMaxBytesPerSec
;
// Maximum data rate of the file
DWORD
dwReserved1
;
// 0
DWORD
dwFlags
;
// 0x10 AVIF_HASINDEX: The AVI file has an idx1 chunk containing an index at the end of the file.
DWORD
dwTotalFrames
;
// Field of the main header specifies the total number of frames of data in file.
DWORD
dwInitialFrames
;
// Is used for interleaved files
DWORD
dwStreams
;
// Specifies the number of streams in the file.
DWORD
dwSuggestedBufferSize
;
// Field specifies the suggested buffer size forreading the file
DWORD
dwWidth
;
// Fields specify the width of the AVIfile in pixels.
DWORD
dwHeight
;
// Fields specify the height of the AVIfile in pixels.
DWORD
dwReserved
[
4
];
// 0, 0, 0, 0
};
struct
AviStreamHeader
{
unsigned
int
fccType
;
// 'vids', 'auds', 'txts'...
unsigned
int
fccHandler
;
// "cvid", "DIB "
DWORD
dwFlags
;
// 0
DWORD
dwPriority
;
// 0
DWORD
dwInitialFrames
;
// 0
DWORD
dwScale
;
// 1
DWORD
dwRate
;
// Fps (dwRate - frame rate for video streams)
DWORD
dwStart
;
// 0
DWORD
dwLength
;
// Frames number (playing time of AVI file as defined by scale and rate)
DWORD
dwSuggestedBufferSize
;
// For reading the stream
DWORD
dwQuality
;
// -1 (encoding quality. If set to -1, drivers use the default quality value)
DWORD
dwSampleSize
;
// 0 means that each frame is in its own chunk
struct
{
short
int
left
;
short
int
top
;
short
int
right
;
short
int
bottom
;
}
rcFrame
;
// If stream has a different size than dwWidth*dwHeight(unused)
};
struct
AviIndex
{
DWORD
ckid
;
DWORD
dwFlags
;
DWORD
dwChunkOffset
;
DWORD
dwChunkLength
;
};
struct
BitmapInfoHeader
{
DWORD
biSize
;
// Write header size of BITMAPINFO header structure
LONG
biWidth
;
// width in pixels
LONG
biHeight
;
// height in pixels
WORD
biPlanes
;
// Number of color planes in which the data is stored
WORD
biBitCount
;
// Number of bits per pixel
DWORD
biCompression
;
// Type of compression used (uncompressed: NO_COMPRESSION=0)
DWORD
biSizeImage
;
// Image Buffer. Quicktime needs 3 bytes also for 8-bit png
// (biCompression==NO_COMPRESSION)?0:xDim*yDim*bytesPerPixel;
LONG
biXPelsPerMeter
;
// Horizontal resolution in pixels per meter
LONG
biYPelsPerMeter
;
// Vertical resolution in pixels per meter
DWORD
biClrUsed
;
// 256 (color table size; for 8-bit only)
DWORD
biClrImportant
;
// Specifies that the first x colors of the color table. Are important to the DIB.
};
struct
RiffChunk
{
unsigned
int
m_four_cc
;
unsigned
int
m_size
;
};
struct
RiffList
{
unsigned
int
m_riff_or_list_cc
;
unsigned
int
m_size
;
unsigned
int
m_list_type_cc
;
};
class
VideoInputStream
{
public
:
VideoInputStream
();
VideoInputStream
(
const
String
&
filename
);
~
VideoInputStream
();
VideoInputStream
&
read
(
char
*
,
unsigned
long
long
int
);
VideoInputStream
&
seekg
(
unsigned
long
long
int
);
unsigned
long
long
int
tellg
();
bool
isOpened
()
const
;
bool
open
(
const
String
&
filename
);
void
close
();
operator
bool
();
VideoInputStream
&
operator
=
(
const
VideoInputStream
&
stream
);
private
:
bool
m_is_valid
;
String
m_fname
;
FILE
*
m_f
;
};
#pragma pack(pop)
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
AviMainHeader
&
avih
)
{
is
.
read
((
char
*
)(
&
avih
),
sizeof
(
AviMainHeader
));
return
is
;
}
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
AviStreamHeader
&
strh
)
{
is
.
read
((
char
*
)(
&
strh
),
sizeof
(
AviStreamHeader
));
return
is
;
}
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
BitmapInfoHeader
&
bmph
)
{
is
.
read
((
char
*
)(
&
bmph
),
sizeof
(
BitmapInfoHeader
));
return
is
;
}
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
AviIndex
&
idx1
)
{
is
.
read
((
char
*
)(
&
idx1
),
sizeof
(
idx1
));
return
is
;
}
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
RiffChunk
&
riff_chunk
)
{
is
.
read
((
char
*
)(
&
riff_chunk
),
sizeof
(
riff_chunk
));
return
is
;
}
inline
VideoInputStream
&
operator
>>
(
VideoInputStream
&
is
,
RiffList
&
riff_list
)
{
is
.
read
((
char
*
)(
&
riff_list
),
sizeof
(
riff_list
));
return
is
;
}
static
const
int
AVIH_STRH_SIZE
=
56
;
static
const
int
STRF_SIZE
=
40
;
static
const
int
AVI_DWFLAG
=
0x00000910
;
static
const
int
AVI_DWSCALE
=
1
;
static
const
int
AVI_DWQUALITY
=
-
1
;
static
const
int
JUNK_SEEK
=
4096
;
static
const
int
AVIIF_KEYFRAME
=
0x10
;
static
const
int
MAX_BYTES_PER_SEC
=
99999999
;
static
const
int
SUG_BUFFER_SIZE
=
1048576
;
String
fourccToString
(
unsigned
int
fourcc
)
{
return
format
(
"%c%c%c%c"
,
fourcc
&
255
,
(
fourcc
>>
8
)
&
255
,
(
fourcc
>>
16
)
&
255
,
(
fourcc
>>
24
)
&
255
);
}
VideoInputStream
::
VideoInputStream
()
:
m_is_valid
(
false
),
m_f
(
0
)
{
m_fname
=
String
();
}
VideoInputStream
::
VideoInputStream
(
const
String
&
filename
)
:
m_is_valid
(
false
),
m_f
(
0
)
{
m_fname
=
filename
;
open
(
filename
);
}
bool
VideoInputStream
::
isOpened
()
const
{
return
m_f
!=
0
;
}
bool
VideoInputStream
::
open
(
const
String
&
filename
)
{
close
();
m_f
=
fopen
(
filename
.
c_str
(),
"rb"
);
m_is_valid
=
isOpened
();
return
m_is_valid
;
}
void
VideoInputStream
::
close
()
{
if
(
isOpened
())
{
m_is_valid
=
false
;
fclose
(
m_f
);
m_f
=
0
;
}
}
VideoInputStream
&
VideoInputStream
::
read
(
char
*
buf
,
unsigned
long
long
int
count
)
{
if
(
isOpened
())
{
m_is_valid
=
(
count
==
fread
((
void
*
)
buf
,
1
,
(
size_t
)
count
,
m_f
));
}
return
*
this
;
}
VideoInputStream
&
VideoInputStream
::
seekg
(
unsigned
long
long
int
pos
)
{
m_is_valid
=
(
fseek
(
m_f
,
(
long
)
pos
,
SEEK_SET
)
==
0
);
return
*
this
;
}
unsigned
long
long
int
VideoInputStream
::
tellg
()
{
return
ftell
(
m_f
);
}
VideoInputStream
::
operator
bool
()
{
return
m_is_valid
;
}
VideoInputStream
&
VideoInputStream
::
operator
=
(
const
VideoInputStream
&
stream
)
{
if
(
this
!=
&
stream
)
{
m_fname
=
stream
.
m_fname
;
// m_f = stream.m_f;
open
(
m_fname
);
}
return
*
this
;
}
VideoInputStream
::~
VideoInputStream
()
{
close
();
}
AVIReadContainer
::
AVIReadContainer
()
:
m_stream_id
(
0
),
m_movi_start
(
0
),
m_movi_end
(
0
),
m_width
(
0
),
m_height
(
0
),
m_fps
(
0
),
m_is_indx_present
(
false
)
{
m_file_stream
=
makePtr
<
VideoInputStream
>
();
}
void
AVIReadContainer
::
initStream
(
const
String
&
filename
)
{
m_file_stream
=
makePtr
<
VideoInputStream
>
(
filename
);
}
void
AVIReadContainer
::
initStream
(
Ptr
<
VideoInputStream
>
m_file_stream_
)
{
m_file_stream
=
m_file_stream_
;
}
void
AVIReadContainer
::
close
()
{
m_file_stream
->
close
();
}
bool
AVIReadContainer
::
parseIndex
(
unsigned
int
index_size
,
frame_list
&
in_frame_list
)
{
unsigned
long
long
int
index_end
=
m_file_stream
->
tellg
();
index_end
+=
index_size
;
bool
result
=
false
;
while
(
m_file_stream
&&
(
m_file_stream
->
tellg
()
<
index_end
))
{
AviIndex
idx1
;
*
m_file_stream
>>
idx1
;
if
(
idx1
.
ckid
==
m_stream_id
)
{
unsigned
long
long
int
absolute_pos
=
m_movi_start
+
idx1
.
dwChunkOffset
;
if
(
absolute_pos
<
m_movi_end
)
{
in_frame_list
.
push_back
(
std
::
make_pair
(
absolute_pos
,
idx1
.
dwChunkLength
));
}
else
{
//unsupported case
fprintf
(
stderr
,
"Frame offset points outside movi section.
\n
"
);
}
}
result
=
true
;
}
return
result
;
}
bool
AVIReadContainer
::
parseStrl
(
char
stream_id
,
Codecs
codec_
)
{
RiffChunk
strh
;
*
m_file_stream
>>
strh
;
if
(
m_file_stream
&&
strh
.
m_four_cc
==
STRH_CC
)
{
unsigned
long
long
int
next_strl_list
=
m_file_stream
->
tellg
();
next_strl_list
+=
strh
.
m_size
;
AviStreamHeader
strm_hdr
;
*
m_file_stream
>>
strm_hdr
;
if
(
codec_
==
MJPEG
)
{
if
(
strm_hdr
.
fccType
==
VIDS_CC
&&
strm_hdr
.
fccHandler
==
MJPG_CC
)
{
char
first_digit
=
(
stream_id
/
10
)
+
'0'
;
char
second_digit
=
(
stream_id
%
10
)
+
'0'
;
if
(
m_stream_id
==
0
)
{
m_stream_id
=
CV_FOURCC
(
first_digit
,
second_digit
,
'd'
,
'c'
);
m_fps
=
double
(
strm_hdr
.
dwRate
)
/
strm_hdr
.
dwScale
;
}
else
{
//second mjpeg video stream found which is not supported
fprintf
(
stderr
,
"More than one video stream found within AVI/AVIX list. Stream %c%cdc would be ignored
\n
"
,
first_digit
,
second_digit
);
}
return
true
;
}
}
}
return
false
;
}
void
AVIReadContainer
::
skipJunk
(
RiffChunk
&
chunk
)
{
if
(
chunk
.
m_four_cc
==
JUNK_CC
)
{
m_file_stream
->
seekg
(
m_file_stream
->
tellg
()
+
chunk
.
m_size
);
*
m_file_stream
>>
chunk
;
}
}
void
AVIReadContainer
::
skipJunk
(
RiffList
&
list
)
{
if
(
list
.
m_riff_or_list_cc
==
JUNK_CC
)
{
//JUNK chunk is 4 bytes less than LIST
m_file_stream
->
seekg
(
m_file_stream
->
tellg
()
+
list
.
m_size
-
4
);
*
m_file_stream
>>
list
;
}
}
bool
AVIReadContainer
::
parseHdrlList
(
Codecs
codec_
)
{
bool
result
=
false
;
RiffChunk
avih
;
*
m_file_stream
>>
avih
;
if
(
m_file_stream
&&
avih
.
m_four_cc
==
AVIH_CC
)
{
unsigned
long
long
int
next_strl_list
=
m_file_stream
->
tellg
();
next_strl_list
+=
avih
.
m_size
;
AviMainHeader
avi_hdr
;
*
m_file_stream
>>
avi_hdr
;
if
(
m_file_stream
)
{
m_is_indx_present
=
((
avi_hdr
.
dwFlags
&
0x10
)
!=
0
);
DWORD
number_of_streams
=
avi_hdr
.
dwStreams
;
CV_Assert
(
number_of_streams
<
0xFF
);
m_width
=
avi_hdr
.
dwWidth
;
m_height
=
avi_hdr
.
dwHeight
;
//the number of strl lists must be equal to number of streams specified in main avi header
for
(
DWORD
i
=
0
;
i
<
number_of_streams
;
++
i
)
{
m_file_stream
->
seekg
(
next_strl_list
);
RiffList
strl_list
;
*
m_file_stream
>>
strl_list
;
if
(
m_file_stream
&&
strl_list
.
m_riff_or_list_cc
==
LIST_CC
&&
strl_list
.
m_list_type_cc
==
STRL_CC
)
{
next_strl_list
=
m_file_stream
->
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_strl_list
+=
(
strl_list
.
m_size
-
4
);
result
=
parseStrl
((
char
)
i
,
codec_
);
}
else
{
printError
(
strl_list
,
STRL_CC
);
}
}
}
}
else
{
printError
(
avih
,
AVIH_CC
);
}
return
result
;
}
bool
AVIReadContainer
::
parseAviWithFrameList
(
frame_list
&
in_frame_list
,
Codecs
codec_
)
{
RiffList
hdrl_list
;
*
m_file_stream
>>
hdrl_list
;
if
(
m_file_stream
&&
hdrl_list
.
m_riff_or_list_cc
==
LIST_CC
&&
hdrl_list
.
m_list_type_cc
==
HDRL_CC
)
{
unsigned
long
long
int
next_list
=
m_file_stream
->
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_list
+=
(
hdrl_list
.
m_size
-
4
);
//parseHdrlList sets m_is_indx_present flag which would be used later
if
(
parseHdrlList
(
codec_
))
{
m_file_stream
->
seekg
(
next_list
);
RiffList
some_list
;
*
m_file_stream
>>
some_list
;
//an optional section INFO
if
(
m_file_stream
&&
some_list
.
m_riff_or_list_cc
==
LIST_CC
&&
some_list
.
m_list_type_cc
==
INFO_CC
)
{
next_list
=
m_file_stream
->
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_list
+=
(
some_list
.
m_size
-
4
);
parseInfo
();
m_file_stream
->
seekg
(
next_list
);
*
m_file_stream
>>
some_list
;
}
//an optional section JUNK
skipJunk
(
some_list
);
//we are expecting to find here movi list. Must present in avi
if
(
m_file_stream
&&
some_list
.
m_riff_or_list_cc
==
LIST_CC
&&
some_list
.
m_list_type_cc
==
MOVI_CC
)
{
bool
is_index_found
=
false
;
m_movi_start
=
m_file_stream
->
tellg
();
m_movi_start
-=
4
;
m_movi_end
=
m_movi_start
+
some_list
.
m_size
;
//if m_is_indx_present is set to true we should find index
if
(
m_is_indx_present
)
{
//we are expecting to find index section after movi list
unsigned
int
indx_pos
=
(
unsigned
int
)
m_movi_start
+
4
;
indx_pos
+=
(
some_list
.
m_size
-
4
);
m_file_stream
->
seekg
(
indx_pos
);
RiffChunk
index_chunk
;
*
m_file_stream
>>
index_chunk
;
if
(
m_file_stream
&&
index_chunk
.
m_four_cc
==
IDX1_CC
)
{
is_index_found
=
parseIndex
(
index_chunk
.
m_size
,
in_frame_list
);
//we are not going anywhere else
}
else
{
printError
(
index_chunk
,
IDX1_CC
);
}
}
//index not present or we were not able to find it
//parsing movi list
if
(
!
is_index_found
)
{
//not implemented
parseMovi
(
in_frame_list
);
fprintf
(
stderr
,
"Failed to parse avi: index was not found
\n
"
);
//we are not going anywhere else
}
}
else
{
printError
(
some_list
,
MOVI_CC
);
}
}
}
else
{
printError
(
hdrl_list
,
HDRL_CC
);
}
return
in_frame_list
.
size
()
>
0
;
}
std
::
vector
<
char
>
AVIReadContainer
::
readFrame
(
frame_iterator
it
)
{
m_file_stream
->
seekg
(
it
->
first
);
RiffChunk
chunk
;
*
(
m_file_stream
)
>>
chunk
;
std
::
vector
<
char
>
result
;
result
.
reserve
(
chunk
.
m_size
);
result
.
resize
(
chunk
.
m_size
);
m_file_stream
->
read
(
&
(
result
[
0
]),
chunk
.
m_size
);
// result.data() failed with MSVS2008
return
result
;
}
bool
AVIReadContainer
::
parseRiff
(
frame_list
&
m_mjpeg_frames_
)
{
bool
result
=
false
;
while
(
*
m_file_stream
)
{
RiffList
riff_list
;
*
m_file_stream
>>
riff_list
;
if
(
*
m_file_stream
&&
riff_list
.
m_riff_or_list_cc
==
RIFF_CC
&&
((
riff_list
.
m_list_type_cc
==
AVI_CC
)
|
(
riff_list
.
m_list_type_cc
==
AVIX_CC
))
)
{
unsigned
long
long
int
next_riff
=
m_file_stream
->
tellg
();
//RiffList::m_size includes fourCC field which we have already read
next_riff
+=
(
riff_list
.
m_size
-
4
);
bool
is_parsed
=
parseAvi
(
m_mjpeg_frames_
,
MJPEG
);
result
=
result
||
is_parsed
;
m_file_stream
->
seekg
(
next_riff
);
}
else
{
break
;
}
}
return
result
;
}
void
AVIReadContainer
::
printError
(
RiffList
&
list
,
unsigned
int
expected_fourcc
)
{
if
(
!
m_file_stream
)
{
fprintf
(
stderr
,
"Unexpected end of file while searching for %s list
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
());
}
else
if
(
list
.
m_riff_or_list_cc
!=
LIST_CC
)
{
fprintf
(
stderr
,
"Unexpected element. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
LIST_CC
).
c_str
(),
fourccToString
(
list
.
m_riff_or_list_cc
).
c_str
());
}
else
{
fprintf
(
stderr
,
"Unexpected list type. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
(),
fourccToString
(
list
.
m_list_type_cc
).
c_str
());
}
}
void
AVIReadContainer
::
printError
(
RiffChunk
&
chunk
,
unsigned
int
expected_fourcc
)
{
if
(
!
m_file_stream
)
{
fprintf
(
stderr
,
"Unexpected end of file while searching for %s chunk
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
());
}
else
{
fprintf
(
stderr
,
"Unexpected element. Expected: %s. Got: %s.
\n
"
,
fourccToString
(
expected_fourcc
).
c_str
(),
fourccToString
(
chunk
.
m_four_cc
).
c_str
());
}
}
class
BitStream
{
public
:
BitStream
();
~
BitStream
()
{
close
();
}
bool
open
(
const
String
&
filename
);
bool
isOpened
()
const
{
return
m_f
!=
0
;
}
void
close
();
void
writeBlock
();
size_t
getPos
()
const
;
void
putByte
(
int
val
);
void
putBytes
(
const
uchar
*
buf
,
int
count
);
void
putShort
(
int
val
);
void
putInt
(
int
val
);
void
jputShort
(
int
val
);
void
patchInt
(
int
val
,
size_t
pos
);
void
jput
(
unsigned
currval
);
void
jflush
(
unsigned
currval
,
int
bitIdx
);
protected
:
std
::
vector
<
uchar
>
m_buf
;
uchar
*
m_start
;
uchar
*
m_end
;
uchar
*
m_current
;
size_t
m_pos
;
bool
m_is_opened
;
FILE
*
m_f
;
};
static
const
size_t
DEFAULT_BLOCK_SIZE
=
(
1
<<
15
);
BitStream
::
BitStream
()
{
m_buf
.
resize
(
DEFAULT_BLOCK_SIZE
+
1024
);
m_start
=
&
m_buf
[
0
];
m_end
=
m_start
+
DEFAULT_BLOCK_SIZE
;
m_is_opened
=
false
;
m_f
=
0
;
m_current
=
0
;
m_pos
=
0
;
}
bool
BitStream
::
open
(
const
String
&
filename
)
{
close
();
m_f
=
fopen
(
filename
.
c_str
(),
"wb"
);
if
(
!
m_f
)
return
false
;
m_current
=
m_start
;
m_pos
=
0
;
return
true
;
}
void
BitStream
::
close
()
{
writeBlock
();
if
(
m_f
)
fclose
(
m_f
);
m_f
=
0
;
}
void
BitStream
::
writeBlock
()
{
size_t
wsz0
=
m_current
-
m_start
;
if
(
wsz0
>
0
&&
m_f
)
{
size_t
wsz
=
fwrite
(
m_start
,
1
,
wsz0
,
m_f
);
CV_Assert
(
wsz
==
wsz0
);
}
m_pos
+=
wsz0
;
m_current
=
m_start
;
}
size_t
BitStream
::
getPos
()
const
{
return
(
size_t
)(
m_current
-
m_start
)
+
m_pos
;
}
void
BitStream
::
putByte
(
int
val
)
{
*
m_current
++
=
(
uchar
)
val
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
BitStream
::
putBytes
(
const
uchar
*
buf
,
int
count
)
{
uchar
*
data
=
(
uchar
*
)
buf
;
CV_Assert
(
m_f
&&
data
&&
m_current
&&
count
>=
0
);
if
(
m_current
>=
m_end
)
writeBlock
();
while
(
count
)
{
int
l
=
(
int
)(
m_end
-
m_current
);
if
(
l
>
count
)
l
=
count
;
if
(
l
>
0
)
{
memcpy
(
m_current
,
data
,
l
);
m_current
+=
l
;
data
+=
l
;
count
-=
l
;
}
if
(
m_current
>=
m_end
)
writeBlock
();
}
}
void
BitStream
::
putShort
(
int
val
)
{
m_current
[
0
]
=
(
uchar
)
val
;
m_current
[
1
]
=
(
uchar
)(
val
>>
8
);
m_current
+=
2
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
BitStream
::
putInt
(
int
val
)
{
m_current
[
0
]
=
(
uchar
)
val
;
m_current
[
1
]
=
(
uchar
)(
val
>>
8
);
m_current
[
2
]
=
(
uchar
)(
val
>>
16
);
m_current
[
3
]
=
(
uchar
)(
val
>>
24
);
m_current
+=
4
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
BitStream
::
jputShort
(
int
val
)
{
m_current
[
0
]
=
(
uchar
)(
val
>>
8
);
m_current
[
1
]
=
(
uchar
)
val
;
m_current
+=
2
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
BitStream
::
patchInt
(
int
val
,
size_t
pos
)
{
if
(
pos
>=
m_pos
)
{
ptrdiff_t
delta
=
pos
-
m_pos
;
CV_Assert
(
delta
<
m_current
-
m_start
);
m_start
[
delta
]
=
(
uchar
)
val
;
m_start
[
delta
+
1
]
=
(
uchar
)(
val
>>
8
);
m_start
[
delta
+
2
]
=
(
uchar
)(
val
>>
16
);
m_start
[
delta
+
3
]
=
(
uchar
)(
val
>>
24
);
}
else
{
long
fpos
=
ftell
(
m_f
);
fseek
(
m_f
,
(
long
)
pos
,
SEEK_SET
);
uchar
buf
[]
=
{
(
uchar
)
val
,
(
uchar
)(
val
>>
8
),
(
uchar
)(
val
>>
16
),
(
uchar
)(
val
>>
24
)
};
fwrite
(
buf
,
1
,
4
,
m_f
);
fseek
(
m_f
,
fpos
,
SEEK_SET
);
}
}
void
BitStream
::
jput
(
unsigned
currval
)
{
uchar
v
;
uchar
*
ptr
=
m_current
;
v
=
(
uchar
)(
currval
>>
24
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)(
currval
>>
16
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)(
currval
>>
8
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
v
=
(
uchar
)
currval
;
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
m_current
=
ptr
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
void
BitStream
::
jflush
(
unsigned
currval
,
int
bitIdx
)
{
uchar
v
;
uchar
*
ptr
=
m_current
;
currval
|=
(
1
<<
bitIdx
)
-
1
;
while
(
bitIdx
<
32
)
{
v
=
(
uchar
)(
currval
>>
24
);
*
ptr
++
=
v
;
if
(
v
==
255
)
*
ptr
++
=
0
;
currval
<<=
8
;
bitIdx
+=
8
;
}
m_current
=
ptr
;
if
(
m_current
>=
m_end
)
writeBlock
();
}
AVIWriteContainer
::
AVIWriteContainer
()
:
strm
(
makePtr
<
BitStream
>
())
{
outfps
=
0
;
height
=
0
;
width
=
0
;
channels
=
0
;
moviPointer
=
0
;
strm
->
close
();
}
AVIWriteContainer
::~
AVIWriteContainer
()
{
strm
->
close
();
frameOffset
.
clear
();
frameSize
.
clear
();
AVIChunkSizeIndex
.
clear
();
frameNumIndexes
.
clear
();
}
bool
AVIWriteContainer
::
initContainer
(
const
String
&
filename
,
double
fps
,
Size
size
,
bool
iscolor
)
{
outfps
=
cvRound
(
fps
);
width
=
size
.
width
;
height
=
size
.
height
;
channels
=
iscolor
?
3
:
1
;
moviPointer
=
0
;
bool
result
=
strm
->
open
(
filename
);
return
result
;
}
void
AVIWriteContainer
::
startWriteAVI
(
int
stream_count
)
{
startWriteChunk
(
RIFF_CC
);
strm
->
putInt
(
AVI_CC
);
startWriteChunk
(
LIST_CC
);
strm
->
putInt
(
HDRL_CC
);
strm
->
putInt
(
AVIH_CC
);
strm
->
putInt
(
AVIH_STRH_SIZE
);
strm
->
putInt
(
cvRound
(
1e6
/
outfps
));
strm
->
putInt
(
MAX_BYTES_PER_SEC
);
strm
->
putInt
(
0
);
strm
->
putInt
(
AVI_DWFLAG
);
frameNumIndexes
.
push_back
(
strm
->
getPos
());
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
stream_count
);
// number of streams
strm
->
putInt
(
SUG_BUFFER_SIZE
);
strm
->
putInt
(
width
);
strm
->
putInt
(
height
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
}
void
AVIWriteContainer
::
writeStreamHeader
(
Codecs
codec_
)
{
// strh
startWriteChunk
(
LIST_CC
);
strm
->
putInt
(
STRL_CC
);
strm
->
putInt
(
STRH_CC
);
strm
->
putInt
(
AVIH_STRH_SIZE
);
strm
->
putInt
(
VIDS_CC
);
switch
(
codec_
)
{
case
MJPEG
:
strm
->
putInt
(
MJPG_CC
);
break
;
}
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
AVI_DWSCALE
);
strm
->
putInt
(
outfps
);
strm
->
putInt
(
0
);
frameNumIndexes
.
push_back
(
strm
->
getPos
());
strm
->
putInt
(
0
);
strm
->
putInt
(
SUG_BUFFER_SIZE
);
strm
->
putInt
(
AVI_DWQUALITY
);
strm
->
putInt
(
0
);
strm
->
putShort
(
0
);
strm
->
putShort
(
0
);
strm
->
putShort
(
width
);
strm
->
putShort
(
height
);
// strf (use the BITMAPINFOHEADER for video)
startWriteChunk
(
STRF_CC
);
strm
->
putInt
(
STRF_SIZE
);
strm
->
putInt
(
width
);
strm
->
putInt
(
height
);
strm
->
putShort
(
1
);
// planes (1 means interleaved data (after decompression))
strm
->
putShort
(
8
*
channels
);
// bits per pixel
switch
(
codec_
)
{
case
MJPEG
:
strm
->
putInt
(
MJPG_CC
);
break
;
}
strm
->
putInt
(
width
*
height
*
channels
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
// Must be indx chunk
endWriteChunk
();
// end strf
endWriteChunk
();
// end strl
// odml
startWriteChunk
(
LIST_CC
);
strm
->
putInt
(
ODML_CC
);
startWriteChunk
(
DMLH_CC
);
frameNumIndexes
.
push_back
(
strm
->
getPos
());
strm
->
putInt
(
0
);
strm
->
putInt
(
0
);
endWriteChunk
();
// end dmlh
endWriteChunk
();
// end odml
endWriteChunk
();
// end hdrl
// JUNK
startWriteChunk
(
JUNK_CC
);
size_t
pos
=
strm
->
getPos
();
for
(
;
pos
<
(
size_t
)
JUNK_SEEK
;
pos
+=
4
)
strm
->
putInt
(
0
);
endWriteChunk
();
// end JUNK
// movi
startWriteChunk
(
LIST_CC
);
moviPointer
=
strm
->
getPos
();
strm
->
putInt
(
MOVI_CC
);
}
void
AVIWriteContainer
::
startWriteChunk
(
int
fourcc
)
{
CV_Assert
(
fourcc
!=
0
);
strm
->
putInt
(
fourcc
);
AVIChunkSizeIndex
.
push_back
(
strm
->
getPos
());
strm
->
putInt
(
0
);
}
void
AVIWriteContainer
::
endWriteChunk
()
{
if
(
!
AVIChunkSizeIndex
.
empty
()
)
{
size_t
currpos
=
strm
->
getPos
();
size_t
pospos
=
AVIChunkSizeIndex
.
back
();
AVIChunkSizeIndex
.
pop_back
();
int
chunksz
=
(
int
)(
currpos
-
(
pospos
+
4
));
strm
->
patchInt
(
chunksz
,
pospos
);
}
}
int
AVIWriteContainer
::
getAVIIndex
(
int
stream_number
,
StreamType
strm_type
)
{
char
strm_indx
[
2
];
strm_indx
[
0
]
=
'0'
+
static_cast
<
char
>
(
stream_number
/
10
);
strm_indx
[
1
]
=
'0'
+
static_cast
<
char
>
(
stream_number
%
10
);
switch
(
strm_type
)
{
case
db
:
return
CV_FOURCC
(
strm_indx
[
0
],
strm_indx
[
1
],
'd'
,
'b'
);
case
dc
:
return
CV_FOURCC
(
strm_indx
[
0
],
strm_indx
[
1
],
'd'
,
'c'
);
case
pc
:
return
CV_FOURCC
(
strm_indx
[
0
],
strm_indx
[
1
],
'p'
,
'c'
);
case
wb
:
return
CV_FOURCC
(
strm_indx
[
0
],
strm_indx
[
1
],
'w'
,
'b'
);
default
:
return
CV_FOURCC
(
strm_indx
[
0
],
strm_indx
[
1
],
'd'
,
'b'
);
}
}
void
AVIWriteContainer
::
writeIndex
(
int
stream_number
,
StreamType
strm_type
)
{
// old style AVI index. Must be Open-DML index
startWriteChunk
(
IDX1_CC
);
int
nframes
=
(
int
)
frameOffset
.
size
();
for
(
int
i
=
0
;
i
<
nframes
;
i
++
)
{
strm
->
putInt
(
getAVIIndex
(
stream_number
,
strm_type
));
strm
->
putInt
(
AVIIF_KEYFRAME
);
strm
->
putInt
((
int
)
frameOffset
[
i
]);
strm
->
putInt
((
int
)
frameSize
[
i
]);
}
endWriteChunk
();
// End idx1
}
void
AVIWriteContainer
::
finishWriteAVI
()
{
int
nframes
=
(
int
)
frameOffset
.
size
();
// Record frames numbers to AVI Header
while
(
!
frameNumIndexes
.
empty
())
{
size_t
ppos
=
frameNumIndexes
.
back
();
frameNumIndexes
.
pop_back
();
strm
->
patchInt
(
nframes
,
ppos
);
}
endWriteChunk
();
// end RIFF
}
bool
AVIWriteContainer
::
isOpenedStream
()
const
{
return
strm
->
isOpened
();
}
size_t
AVIWriteContainer
::
getStreamPos
()
const
{
return
strm
->
getPos
();
}
void
AVIWriteContainer
::
jputStreamShort
(
int
val
)
{
strm
->
jputShort
(
val
);
}
void
AVIWriteContainer
::
putStreamBytes
(
const
uchar
*
buf
,
int
count
)
{
strm
->
putBytes
(
buf
,
count
);
}
void
AVIWriteContainer
::
putStreamByte
(
int
val
)
{
strm
->
putByte
(
val
);
}
void
AVIWriteContainer
::
jputStream
(
unsigned
currval
)
{
strm
->
jput
(
currval
);
}
void
AVIWriteContainer
::
jflushStream
(
unsigned
currval
,
int
bitIdx
)
{
strm
->
jflush
(
currval
,
bitIdx
);
}
}
modules/videoio/test/test_container_avi.cpp
0 → 100644
View file @
30b33a44
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
#include "opencv2/videoio/container_avi.private.hpp"
#include <cstdio>
using
namespace
cv
;
namespace
opencv_test
{
TEST
(
videoio_avi
,
good_MJPG
)
{
String
filename
=
BunnyParameters
::
getFilename
(
".mjpg.avi"
);
AVIReadContainer
in
;
in
.
initStream
(
filename
);
frame_list
frames
;
ASSERT_TRUE
(
in
.
parseRiff
(
frames
));
EXPECT_EQ
(
frames
.
size
(),
static_cast
<
unsigned
>
(
BunnyParameters
::
getCount
()));
EXPECT_EQ
(
in
.
getWidth
(),
static_cast
<
unsigned
>
(
BunnyParameters
::
getWidth
()));
EXPECT_EQ
(
in
.
getHeight
(),
static_cast
<
unsigned
>
(
BunnyParameters
::
getHeight
()));
EXPECT_EQ
(
in
.
getFps
(),
static_cast
<
unsigned
>
(
BunnyParameters
::
getFps
()));
}
TEST
(
videoio_avi
,
bad_MJPG
)
{
String
filename
=
BunnyParameters
::
getFilename
(
".avi"
);
AVIReadContainer
in
;
in
.
initStream
(
filename
);
frame_list
frames
;
EXPECT_FALSE
(
in
.
parseRiff
(
frames
));
EXPECT_EQ
(
frames
.
size
(),
static_cast
<
unsigned
>
(
0
));
}
TEST
(
videoio_avi
,
basic
)
{
const
String
filename
=
cv
::
tempfile
(
"test.avi"
);
const
double
fps
=
100
;
const
Size
sz
(
800
,
600
);
const
size_t
count
=
10
;
const
uchar
data
[
count
]
=
{
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
0xA
};
const
Codecs
codec
=
MJPEG
;
{
AVIWriteContainer
out
;
ASSERT_TRUE
(
out
.
initContainer
(
filename
,
fps
,
sz
,
true
));
ASSERT_TRUE
(
out
.
isOpenedStream
());
EXPECT_EQ
(
out
.
getWidth
(),
sz
.
width
);
EXPECT_EQ
(
out
.
getHeight
(),
sz
.
height
);
EXPECT_EQ
(
out
.
getChannels
(),
3
);
out
.
startWriteAVI
(
1
);
{
out
.
writeStreamHeader
(
codec
);
// starts LIST chunk
size_t
chunkPointer
=
out
.
getStreamPos
();
int
avi_index
=
out
.
getAVIIndex
(
0
,
dc
);
{
out
.
startWriteChunk
(
avi_index
);
out
.
putStreamBytes
(
data
,
count
);
size_t
tempChunkPointer
=
out
.
getStreamPos
();
size_t
moviPointer
=
out
.
getMoviPointer
();
out
.
pushFrameOffset
(
chunkPointer
-
moviPointer
);
out
.
pushFrameSize
(
tempChunkPointer
-
chunkPointer
-
8
);
out
.
endWriteChunk
();
}
out
.
endWriteChunk
();
// ends LIST chunk
}
out
.
writeIndex
(
0
,
dc
);
out
.
finishWriteAVI
();
}
{
AVIReadContainer
in
;
in
.
initStream
(
filename
);
frame_list
frames
;
ASSERT_TRUE
(
in
.
parseRiff
(
frames
));
EXPECT_EQ
(
in
.
getFps
(),
fps
);
EXPECT_EQ
(
in
.
getWidth
(),
static_cast
<
unsigned
>
(
sz
.
width
));
EXPECT_EQ
(
in
.
getHeight
(),
static_cast
<
unsigned
>
(
sz
.
height
));
ASSERT_EQ
(
frames
.
size
(),
static_cast
<
unsigned
>
(
1
));
std
::
vector
<
char
>
actual
=
in
.
readFrame
(
frames
.
begin
());
ASSERT_EQ
(
actual
.
size
(),
count
);
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
EXPECT_EQ
(
actual
.
at
(
i
),
data
[
i
])
<<
"at index "
<<
i
;
}
remove
(
filename
.
c_str
());
}
}
modules/videoio/test/test_precomp.hpp
View file @
30b33a44
...
...
@@ -41,4 +41,18 @@ inline void generateFrame(int i, int FRAME_COUNT, cv::Mat & frame)
#endif
}
class
BunnyParameters
{
public
:
inline
static
int
getWidth
()
{
return
672
;
};
inline
static
int
getHeight
()
{
return
384
;
};
inline
static
int
getFps
()
{
return
24
;
};
inline
static
double
getTime
()
{
return
5.21
;
};
inline
static
int
getCount
()
{
return
cvRound
(
getFps
()
*
getTime
());
};
inline
static
std
::
string
getFilename
(
const
std
::
string
&
ext
)
{
return
cvtest
::
TS
::
ptr
()
->
get_data_path
()
+
"video/big_buck_bunny"
+
ext
;
}
};
#endif
modules/videoio/test/test_video_io.cpp
View file @
30b33a44
...
...
@@ -151,13 +151,13 @@ typedef tuple<string, int> Backend_Type_Params;
class
Videoio_Bunny
:
public
Videoio_Test_Base
,
public
testing
::
TestWithParam
<
Backend_Type_Params
>
{
BunnyParameters
bunny_param
;
public
:
Videoio_Bunny
()
{
ext
=
get
<
0
>
(
GetParam
());
apiPref
=
get
<
1
>
(
GetParam
());
video_file
=
cvtest
::
TS
::
ptr
()
->
get_data_path
()
+
"video/big_buck_bunny."
+
ext
;
video_file
=
BunnyParameters
::
getFilename
(
String
(
"."
)
+
ext
);
}
void
doFrameCountTest
()
{
...
...
@@ -181,18 +181,12 @@ public:
return
;
}
const
int
width_gt
=
672
;
const
int
height_gt
=
384
;
const
int
fps_gt
=
24
;
const
double
time_gt
=
5.21
;
const
int
count_gt
=
cvRound
(
fps_gt
*
time_gt
);
// 5.21 sec * 24 fps
EXPECT_EQ
(
width_gt
,
cap
.
get
(
CAP_PROP_FRAME_WIDTH
));
EXPECT_EQ
(
height_gt
,
cap
.
get
(
CAP_PROP_FRAME_HEIGHT
));
EXPECT_EQ
(
bunny_param
.
getWidth
()
,
cap
.
get
(
CAP_PROP_FRAME_WIDTH
));
EXPECT_EQ
(
bunny_param
.
getHeight
(),
cap
.
get
(
CAP_PROP_FRAME_HEIGHT
));
double
fps_prop
=
cap
.
get
(
CAP_PROP_FPS
);
if
(
fps_prop
>
0
)
EXPECT_NEAR
(
fps_prop
,
fps_gt
,
1
);
EXPECT_NEAR
(
fps_prop
,
bunny_param
.
getFps
()
,
1
);
else
std
::
cout
<<
"FPS is not available. SKIP check."
<<
std
::
endl
;
...
...
@@ -204,7 +198,7 @@ public:
{
if
(
count_prop
>
0
)
{
EXPECT_EQ
(
count_gt
,
count_prop
);
EXPECT_EQ
(
bunny_param
.
getCount
()
,
count_prop
);
}
}
...
...
@@ -215,13 +209,13 @@ public:
cap
>>
frame
;
if
(
frame
.
empty
())
break
;
EXPECT_EQ
(
width_gt
,
frame
.
cols
);
EXPECT_EQ
(
height_gt
,
frame
.
rows
);
EXPECT_EQ
(
bunny_param
.
getWidth
()
,
frame
.
cols
);
EXPECT_EQ
(
bunny_param
.
getHeight
()
,
frame
.
rows
);
count_actual
+=
1
;
}
if
(
count_prop
>
0
)
{
EXPECT_NEAR
(
count_gt
,
count_actual
,
1
);
EXPECT_NEAR
(
bunny_param
.
getCount
()
,
count_actual
,
1
);
}
else
std
::
cout
<<
"Frames counter is not available. Actual frames: "
<<
count_actual
<<
". SKIP check."
<<
std
::
endl
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment