diff --git a/modules/videoio/src/cap_ffmpeg.cpp b/modules/videoio/src/cap_ffmpeg.cpp index 62f64cb0f7..cc62920c74 100644 --- a/modules/videoio/src/cap_ffmpeg.cpp +++ b/modules/videoio/src/cap_ffmpeg.cpp @@ -194,7 +194,8 @@ public: } } - icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image.getMat().ptr(), (int)image.step(), image.cols(), image.rows(), image.channels(), 0); + if (!icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image.getMat().ptr(), (int)image.step(), image.cols(), image.rows(), image.channels(), 0)) + CV_LOG_WARNING(NULL, "FFmpeg: Failed to write frame"); } virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params ) { diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 4ec2137a39..9cd20fbf77 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -2525,11 +2525,13 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int // check parameters if (input_pix_fmt == AV_PIX_FMT_BGR24) { if (cn != 3) { + CV_LOG_WARNING(NULL, "write frame skipped - expected 3 channels but got " << cn); return false; } } else if (input_pix_fmt == AV_PIX_FMT_GRAY8 || input_pix_fmt == AV_PIX_FMT_GRAY16LE) { if (cn != 1) { + CV_LOG_WARNING(NULL, "write frame skipped - expected 1 channel but got " << cn); return false; } } @@ -2640,14 +2642,16 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int } hw_frame->pts = frame_idx; int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, hw_frame, frame_idx); - ret = ret_write >= 0 ? true : false; + // AVERROR(EAGAIN): continue sending input, not an error + ret = (ret_write >= 0 || ret_write == AVERROR(EAGAIN)); av_frame_free(&hw_frame); } else #endif { picture->pts = frame_idx; int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, picture, frame_idx); - ret = ret_write >= 0 ? true : false; + // AVERROR(EAGAIN): continue sending input, not an error + ret = (ret_write >= 0 || ret_write == AVERROR(EAGAIN)); } frame_idx++; diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index a158119d3f..ff1ff2b028 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -2699,7 +2699,7 @@ void CvVideoWriter_GStreamer::write(InputArray image) } else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY16_LE) { if (image.type() != CV_16UC1) { - CV_WARN("write frame skipped - expected CV_16UC3"); + CV_WARN("write frame skipped - expected CV_16UC1"); return; } } diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 03e41bd8f0..eeb0835078 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -953,4 +953,81 @@ inline static std::string videoio_ffmpeg_16bit_name_printer(const testing::TestP INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_16bit, testing::ValuesIn(sixteen_bit_modes), videoio_ffmpeg_16bit_name_printer); +typedef tuple ChannelMismatchTestParams; +typedef testing::TestWithParam< ChannelMismatchTestParams > videoio_ffmpeg_channel_mismatch; + +TEST_P(videoio_ffmpeg_channel_mismatch, basic) +{ + if (!videoio_registry::hasBackend(CAP_FFMPEG)) + throw SkipTestException("FFmpeg backend was not found"); + + const string filename = "mismatch_video.mp4"; + int input_type = get<0>(GetParam()); + int depth = get<1>(GetParam()); + bool is_Color = get<2>(GetParam()); + bool is_valid = get<3>(GetParam()); + const string description = get<4>(GetParam()); + + const double fps = 15.0; + const int fourcc = VideoWriter::fourcc('m', 'p', '4', 'v'); + const Mat frame(480, 640, input_type, Scalar::all(0)); + + VideoWriter writer(filename, fourcc, fps, frame.size(), + { + cv::VIDEOWRITER_PROP_DEPTH, depth, + VIDEOWRITER_PROP_IS_COLOR, is_Color + }); + + if (!writer.isOpened()) + throw SkipTestException("Failed to open video writer"); + + for (int i = 1; i <= 15; i++) + { + // In case of mismatch between input frame channels and + // expected depth/isColor configuration a warning should be printed communicating it + writer.write(frame); + } + + writer.release(); + + VideoCapture cap(filename, CAP_FFMPEG); + + if (is_valid) { + ASSERT_TRUE(cap.isOpened()) << "Can't open video for " << description; + EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 15) << "All frames should be written for: " << description; + } else { + ASSERT_FALSE(cap.isOpened()) << "Video capture should fail to open for: " << description; + } + + std::remove(filename.c_str()); +} + +const ChannelMismatchTestParams mismatch_cases[] = +{ + // Testing input frame channels and expected depth/isColor combinations + + // Open VideoWriter depth/isColor combination: CV_8U/true, everything with 3 channels should be valid + make_tuple(CV_16UC1, CV_8U, true, false, "input_CV_16UC1_expected_CV_8U_isColor_true"), + make_tuple(CV_8UC1, CV_8U, true, false, "input_CV_8UC1_expected_CV_8U_isColor_true"), + make_tuple(CV_8UC3, CV_8U, true, true, "input_CV_8UC3_expected_CV_8U_isColor_true_valid"), + make_tuple(CV_16UC3, CV_8U, true, true, "input_CV_16UC3_expected_CV_8U_isColor_true_valid"), + + // Open VideoWriter depth/isColor combination: 16U,8U/false, everything with 1 channel should be valid + make_tuple(CV_8UC3, CV_8U, false, false, "input_CV_8UC3_expected_CV_8U_isColor_false"), + make_tuple(CV_16UC3, CV_8U, false, false, "input_CV_16UC3_expected_CV_8U_isColor_false"), + make_tuple(CV_8UC3, CV_16U, false, false, "input_CV_8UC3_expected_CV_16U_isColor_false"), + make_tuple(CV_16UC3, CV_16U, false, false, "input_CV_16UC3_expected_CV_16U_isColor_false"), + make_tuple(CV_8UC1, CV_16U, false, true, "input_CV_8UC1_expected_CV_16U_isColor_false_valid"), + make_tuple(CV_16UC1, CV_8U, false, true, "input_CV_16UC1_expected_CV_8U_isColor_false_valid"), +}; + +inline static std::string videoio_ffmpeg_mismatch_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << get<4>(info.param); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_channel_mismatch, testing::ValuesIn(mismatch_cases), videoio_ffmpeg_mismatch_name_printer); + }} // namespace