Commit df8f6187 authored by Alexander Nesterov's avatar Alexander Nesterov

Added avi-container with tests

parent cff79609
......@@ -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
......
// 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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// 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());
}
}
......@@ -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
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment