Skip to content

Commit 5bfa43f

Browse files
committed
Merge pull request opencv#17489 from Lapshin:ffmpeg_cap_consider_rotation_metadata_3.4
2 parents ef1690e + 7ed37b3 commit 5bfa43f

File tree

5 files changed

+133
-7
lines changed

5 files changed

+133
-7
lines changed

modules/videoio/include/opencv2/videoio.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ enum VideoCaptureProperties {
177177
CAP_PROP_WB_TEMPERATURE=45, //!< white-balance color temperature
178178
CAP_PROP_CODEC_PIXEL_FORMAT =46, //!< (read-only) codec's pixel format. 4-character code - see VideoWriter::fourcc . Subset of [AV_PIX_FMT_*](https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/raw.c) or -1 if unknown
179179
CAP_PROP_BITRATE =47, //!< (read-only) Video bitrate in kbits/s
180+
CAP_PROP_ORIENTATION_META=48, //!< (read-only) Frame rotation defined by stream meta (applicable for FFmpeg back-end only)
181+
CAP_PROP_ORIENTATION_AUTO=49, //!< if true - rotates output frames of CvCapture considering video file's metadata (applicable for FFmpeg back-end only) (https://github.com/opencv/opencv/issues/15499)
180182
#ifndef CV_DOXYGEN
181183
CV__CAP_PROP_LATEST
182184
#endif

modules/videoio/src/cap_ffmpeg.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,11 @@ class CvCapture_FFMPEG_proxy CV_FINAL : public cv::IVideoCapture
235235
if (!ffmpegCapture ||
236236
!icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn))
237237
return false;
238-
cv::Mat(height, width, CV_MAKETYPE(CV_8U, cn), data, step).copyTo(frame);
238+
239+
cv::Mat tmp(height, width, CV_MAKETYPE(CV_8U, cn), data, step);
240+
this->rotateFrame(tmp);
241+
tmp.copyTo(frame);
242+
239243
return true;
240244
}
241245
virtual bool open( const cv::String& filename )
@@ -262,6 +266,30 @@ class CvCapture_FFMPEG_proxy CV_FINAL : public cv::IVideoCapture
262266

263267
protected:
264268
CvCapture_FFMPEG* ffmpegCapture;
269+
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+
}
265293
};
266294

267295
} // namespace

modules/videoio/src/cap_ffmpeg_api.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ enum
2828
CV_FFMPEG_CAP_PROP_SAR_NUM=40,
2929
CV_FFMPEG_CAP_PROP_SAR_DEN=41,
3030
CV_FFMPEG_CAP_PROP_CODEC_PIXEL_FORMAT=46,
31-
CV_FFMPEG_CAP_PROP_BITRATE=47
31+
CV_FFMPEG_CAP_PROP_BITRATE=47,
32+
CV_FFMPEG_CAP_PROP_ORIENTATION_META=48,
33+
CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO=49
3234
};
3335

3436
typedef struct CvCapture_FFMPEG CvCapture_FFMPEG;

modules/videoio/src/cap_ffmpeg_impl.hpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ struct CvCapture_FFMPEG
482482
bool setProperty(int, double);
483483
bool grabFrame();
484484
bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn);
485+
void rotateFrame(cv::Mat &mat) const;
485486

486487
void init();
487488

@@ -497,6 +498,7 @@ struct CvCapture_FFMPEG
497498
double r2d(AVRational r) const;
498499
int64_t dts_to_frame_number(int64_t dts);
499500
double dts_to_sec(int64_t dts) const;
501+
void get_rotation_angle();
500502

501503
AVFormatContext * ic;
502504
AVCodec * avcodec;
@@ -512,6 +514,8 @@ struct CvCapture_FFMPEG
512514

513515
int64_t frame_number, first_frame_number;
514516

517+
bool rotation_auto;
518+
int rotation_angle; // valid 0, 90, 180, 270
515519
double eps_zero;
516520
/*
517521
'filename' contains the filename of the videosource,
@@ -560,8 +564,17 @@ void CvCapture_FFMPEG::init()
560564
frame_number = 0;
561565
eps_zero = 0.000025;
562566

563-
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
567+
rotation_angle = 0;
568+
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
564575
dict = NULL;
576+
#else
577+
rotation_auto = false;
565578
#endif
566579

567580
rawMode = false;
@@ -1032,6 +1045,7 @@ bool CvCapture_FFMPEG::open( const char* _filename )
10321045
frame.cn = 3;
10331046
frame.step = 0;
10341047
frame.data = NULL;
1048+
get_rotation_angle();
10351049
break;
10361050
}
10371051
}
@@ -1290,7 +1304,6 @@ bool CvCapture_FFMPEG::grabFrame()
12901304
return valid;
12911305
}
12921306

1293-
12941307
bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn)
12951308
{
12961309
if (!video_st)
@@ -1376,7 +1389,6 @@ bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int*
13761389
return true;
13771390
}
13781391

1379-
13801392
double CvCapture_FFMPEG::getProperty( int property_id ) const
13811393
{
13821394
if( !video_st ) return 0;
@@ -1400,9 +1412,9 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
14001412
case CV_FFMPEG_CAP_PROP_FRAME_COUNT:
14011413
return (double)get_total_frames();
14021414
case CV_FFMPEG_CAP_PROP_FRAME_WIDTH:
1403-
return (double)frame.width;
1415+
return (double)((rotation_auto && rotation_angle%180) ? frame.height : frame.width);
14041416
case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT:
1405-
return (double)frame.height;
1417+
return (double)((rotation_auto && rotation_angle%180) ? frame.width : frame.height);
14061418
case CV_FFMPEG_CAP_PROP_FPS:
14071419
return get_fps();
14081420
case CV_FFMPEG_CAP_PROP_FOURCC:
@@ -1446,6 +1458,15 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
14461458
break;
14471459
case CV_FFMPEG_CAP_PROP_BITRATE:
14481460
return static_cast<double>(get_bitrate());
1461+
case CV_FFMPEG_CAP_PROP_ORIENTATION_META:
1462+
return static_cast<double>(rotation_angle);
1463+
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
1464+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1465+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
1466+
return static_cast<double>(rotation_auto);
1467+
#else
1468+
return 0;
1469+
#endif
14491470
default:
14501471
break;
14511472
}
@@ -1524,6 +1545,17 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
15241545
r2d(ic->streams[video_stream]->time_base);
15251546
}
15261547

1548+
void CvCapture_FFMPEG::get_rotation_angle()
1549+
{
1550+
rotation_angle = 0;
1551+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1552+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
1553+
AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
1554+
if (rotate_tag != NULL)
1555+
rotation_angle = atoi(rotate_tag->value);
1556+
#endif
1557+
}
1558+
15271559
void CvCapture_FFMPEG::seek(int64_t _frame_number)
15281560
{
15291561
_frame_number = std::min(_frame_number, get_total_frames());
@@ -1619,6 +1651,16 @@ bool CvCapture_FFMPEG::setProperty( int property_id, double value )
16191651
if (value == -1)
16201652
return setRaw();
16211653
return false;
1654+
case CV_FFMPEG_CAP_PROP_ORIENTATION_AUTO:
1655+
#if ((LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)) && \
1656+
(LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)))
1657+
rotation_auto = static_cast<bool>(value);
1658+
return true;
1659+
#else
1660+
rotation_auto = 0;
1661+
return false;
1662+
#endif
1663+
break;
16221664
default:
16231665
return false;
16241666
}

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)