Skip to content

Commit 7ed37b3

Browse files
committed
MP4 autorotation tests and various fixes for Windows
- Added test for automated rotation for MP4 videos with metadata - Fix 180 degrees rotation bug - Moved rotation logic to cv::VideoCapture implementation for FFmpeg and restore binary compatibility with FFmpeg wrapper.
1 parent f0271e5 commit 7ed37b3

File tree

4 files changed

+128
-40
lines changed

4 files changed

+128
-40
lines changed

modules/videoio/src/cap_ffmpeg.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,17 @@ class CvCapture_FFMPEG_proxy CV_FINAL : public cv::IVideoCapture
229229
}
230230
virtual bool retrieveFrame(int, cv::OutputArray frame) CV_OVERRIDE
231231
{
232-
cv::Mat mat;
232+
unsigned char* data = 0;
233+
int step=0, width=0, height=0, cn=0;
234+
233235
if (!ffmpegCapture ||
234-
!icvRetrieveFrame_FFMPEG_p(ffmpegCapture, mat))
236+
!icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn))
235237
return false;
236238

237-
mat.copyTo(frame);
239+
cv::Mat tmp(height, width, CV_MAKETYPE(CV_8U, cn), data, step);
240+
this->rotateFrame(tmp);
241+
tmp.copyTo(frame);
242+
238243
return true;
239244
}
240245
virtual bool open( const cv::String& filename )
@@ -261,8 +266,30 @@ class CvCapture_FFMPEG_proxy CV_FINAL : public cv::IVideoCapture
261266

262267
protected:
263268
CvCapture_FFMPEG* ffmpegCapture;
264-
private:
265269

270+
void rotateFrame(cv::Mat &mat) const
271+
{
272+
bool rotation_auto = 0 != getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO);
273+
int rotation_angle = static_cast<int>(getProperty(CV_FFMPEG_CAP_PROP_ORIENTATION_META));
274+
275+
if(!rotation_auto || rotation_angle%360 == 0)
276+
{
277+
return;
278+
}
279+
280+
cv::RotateFlags flag;
281+
if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees
282+
flag = cv::ROTATE_90_CLOCKWISE;
283+
} else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees
284+
flag = cv::ROTATE_90_COUNTERCLOCKWISE;
285+
} else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees
286+
flag = cv::ROTATE_180;
287+
} else { // Unsupported rotation
288+
return;
289+
}
290+
291+
cv::rotate(mat, mat, flag);
292+
}
266293
};
267294

268295
} // namespace

modules/videoio/src/cap_ffmpeg_api.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap,
4141
int prop, double value);
4242
OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop);
4343
OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap);
44-
OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, cv::Mat &mat);
44+
OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data,
45+
int* step, int* width, int* height, int* cn);
4546
OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap);
4647

4748
OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename,
@@ -53,7 +54,8 @@ OPENCV_FFMPEG_API void cvReleaseVideoWriter_FFMPEG(struct CvVideoWriter_FFMPEG**
5354
typedef CvCapture_FFMPEG* (*CvCreateFileCapture_Plugin)( const char* filename );
5455
typedef CvCapture_FFMPEG* (*CvCreateCameraCapture_Plugin)( int index );
5556
typedef int (*CvGrabFrame_Plugin)( CvCapture_FFMPEG* capture_handle );
56-
typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, cv::Mat &mat);
57+
typedef int (*CvRetrieveFrame_Plugin)( CvCapture_FFMPEG* capture_handle, unsigned char** data, int* step,
58+
int* width, int* height, int* cn );
5759
typedef int (*CvSetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id, double value );
5860
typedef double (*CvGetCaptureProperty_Plugin)( CvCapture_FFMPEG* capture_handle, int prop_id );
5961
typedef void (*CvReleaseCapture_Plugin)( CvCapture_FFMPEG** capture_handle );

modules/videoio/src/cap_ffmpeg_impl.hpp

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ struct CvCapture_FFMPEG
481481
double getProperty(int) const;
482482
bool setProperty(int, double);
483483
bool grabFrame();
484-
bool retrieveFrame(int, cv::Mat &mat);
484+
bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn);
485485
void rotateFrame(cv::Mat &mat) const;
486486

487487
void init();
@@ -564,11 +564,17 @@ void CvCapture_FFMPEG::init()
564564
frame_number = 0;
565565
eps_zero = 0.000025;
566566

567-
rotation_auto = true;
568567
rotation_angle = 0;
569568

570-
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
569+
#if (LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0))
570+
#if (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 92, 100))
571+
rotation_auto = true;
572+
#else
573+
rotation_auto = false;
574+
#endif
571575
dict = NULL;
576+
#else
577+
rotation_auto = false;
572578
#endif
573579

574580
rawMode = false;
@@ -1287,19 +1293,20 @@ bool CvCapture_FFMPEG::grabFrame()
12871293
return valid;
12881294
}
12891295

1290-
bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat)
1296+
bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn)
12911297
{
12921298
if (!video_st)
12931299
return false;
12941300

12951301
if (rawMode)
12961302
{
12971303
AVPacket& p = bsfc ? packet_filtered : packet;
1298-
if (p.data == NULL)
1299-
return false;
1300-
1301-
mat = cv::Mat(1, p.size, CV_MAKETYPE(CV_8U, 1), p.data, p.size);
1302-
return true;
1304+
*data = p.data;
1305+
*step = p.size;
1306+
*width = p.size;
1307+
*height = 1;
1308+
*cn = 1;
1309+
return p.data != NULL;
13031310
}
13041311

13051312
if (!picture->data[0])
@@ -1362,28 +1369,13 @@ bool CvCapture_FFMPEG::retrieveFrame(int, cv::Mat &mat)
13621369
rgb_picture.linesize
13631370
);
13641371

1365-
mat = cv::Mat(frame.height, frame.width, CV_MAKETYPE(CV_8U, frame.cn), frame.data, frame.step);
1366-
rotateFrame(mat);
1367-
return true;
1368-
}
1369-
1370-
void CvCapture_FFMPEG::rotateFrame(cv::Mat &mat) const {
1371-
if(!rotation_auto || rotation_angle%360 == 0) {
1372-
return;
1373-
}
1374-
1375-
cv::RotateFlags flag;
1376-
if(rotation_angle == 90 || rotation_angle == -270) { // Rotate clockwise 90 degrees
1377-
flag = cv::ROTATE_90_CLOCKWISE;
1378-
} else if(rotation_angle == 270 || rotation_angle == -90) { // Rotate clockwise 270 degrees
1379-
flag = cv::ROTATE_90_COUNTERCLOCKWISE;
1380-
} else if(rotation_angle == 180 || rotation_angle == -180) { // Rotate clockwise 180 degrees
1381-
flag = cv::ROTATE_180;
1382-
} else { // Unsupported rotation
1383-
return;
1384-
}
1372+
*data = frame.data;
1373+
*step = frame.step;
1374+
*width = frame.width;
1375+
*height = frame.height;
1376+
*cn = frame.cn;
13851377

1386-
cv::rotate(mat, mat, flag);
1378+
return true;
13871379
}
13881380

13891381
double CvCapture_FFMPEG::getProperty( int property_id ) const
@@ -1409,9 +1401,9 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
14091401
case CV_FFMPEG_CAP_PROP_FRAME_COUNT:
14101402
return (double)get_total_frames();
14111403
case CV_FFMPEG_CAP_PROP_FRAME_WIDTH:
1412-
return (double)((rotation_auto && rotation_angle%360) ? frame.height : frame.width);
1404+
return (double)((rotation_auto && rotation_angle%180) ? frame.height : frame.width);
14131405
case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT:
1414-
return (double)((rotation_auto && rotation_angle%360) ? frame.width : frame.height);
1406+
return (double)((rotation_auto && rotation_angle%180) ? frame.width : frame.height);
14151407
case CV_FFMPEG_CAP_PROP_FPS:
14161408
return get_fps();
14171409
case CV_FFMPEG_CAP_PROP_FOURCC:
@@ -1458,7 +1450,12 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
14581450
case CV_FFMPEG_CAP_PROP_ORIENTATION_META:
14591451
return static_cast<double>(rotation_angle);
14601452
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
1453+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1454+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
14611455
return static_cast<double>(rotation_auto);
1456+
#else
1457+
return 0;
1458+
#endif
14621459
default:
14631460
break;
14641461
}
@@ -1540,9 +1537,12 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
15401537
void CvCapture_FFMPEG::get_rotation_angle()
15411538
{
15421539
rotation_angle = 0;
1540+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1541+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
15431542
AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
15441543
if (rotate_tag != NULL)
15451544
rotation_angle = atoi(rotate_tag->value);
1545+
#endif
15461546
}
15471547

15481548
void CvCapture_FFMPEG::seek(int64_t _frame_number)
@@ -1641,7 +1641,14 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value )
16411641
return setRaw();
16421642
return false;
16431643
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
1644+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1645+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
16441646
rotation_auto = static_cast<bool>(value);
1647+
return true;
1648+
#else
1649+
rotation_auto = 0;
1650+
return false;
1651+
#endif
16451652
break;
16461653
default:
16471654
return false;
@@ -2670,9 +2677,9 @@ int cvGrabFrame_FFMPEG(CvCapture_FFMPEG* capture)
26702677
return capture->grabFrame();
26712678
}
26722679

2673-
int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, cv::Mat &mat)
2680+
int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, unsigned char** data, int* step, int* width, int* height, int* cn)
26742681
{
2675-
return capture->retrieveFrame(0, mat);
2682+
return capture->retrieveFrame(0, data, step, width, height, cn);
26762683
}
26772684

26782685
CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps,

modules/videoio/test/test_ffmpeg.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,5 +636,57 @@ const ffmpeg_cap_properties_param_t videoio_ffmpeg_properties[] = {
636636

637637
INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_cap_properties, testing::ValuesIn(videoio_ffmpeg_properties));
638638

639+
// related issue: https://github.com/opencv/opencv/issues/15499
640+
TEST(videoio, mp4_orientation_meta_auto)
641+
{
642+
string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4";
643+
644+
VideoCapture cap;
645+
EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
646+
ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl;
647+
648+
cap.set(CAP_PROP_ORIENTATION_AUTO, true);
649+
if (cap.get(CAP_PROP_ORIENTATION_AUTO) == 0)
650+
throw SkipTestException("FFmpeg frame rotation metadata is not supported");
651+
652+
Size actual;
653+
EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
654+
(int)cap.get(CAP_PROP_FRAME_HEIGHT)));
655+
EXPECT_EQ(384, actual.width);
656+
EXPECT_EQ(672, actual.height);
657+
658+
Mat frame;
659+
660+
cap >> frame;
661+
662+
ASSERT_EQ(384, frame.cols);
663+
ASSERT_EQ(672, frame.rows);
664+
}
665+
666+
// related issue: https://github.com/opencv/opencv/issues/15499
667+
TEST(videoio, mp4_orientation_no_rotation)
668+
{
669+
string video_file = string(cvtest::TS::ptr()->get_data_path()) + "video/big_buck_bunny_rotated.mp4";
670+
671+
VideoCapture cap;
672+
EXPECT_NO_THROW(cap.open(video_file, CAP_FFMPEG));
673+
cap.set(CAP_PROP_ORIENTATION_AUTO, 0);
674+
ASSERT_TRUE(cap.isOpened()) << "Can't open the video: " << video_file << " with backend " << CAP_FFMPEG << std::endl;
675+
ASSERT_FALSE(cap.get(CAP_PROP_ORIENTATION_AUTO));
676+
677+
Size actual;
678+
EXPECT_NO_THROW(actual = Size((int)cap.get(CAP_PROP_FRAME_WIDTH),
679+
(int)cap.get(CAP_PROP_FRAME_HEIGHT)));
680+
EXPECT_EQ(672, actual.width);
681+
EXPECT_EQ(384, actual.height);
682+
683+
Mat frame;
684+
685+
cap >> frame;
686+
687+
ASSERT_EQ(672, frame.cols);
688+
ASSERT_EQ(384, frame.rows);
689+
}
690+
639691
#endif
640692
}} // namespace

0 commit comments

Comments
 (0)