chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
#include "../test/common/gapi_tests_common.hpp"
|
||||
|
||||
#include "../src/streaming/gstreamer/gstreamer_pipeline_facade.hpp"
|
||||
#include "../src/streaming/gstreamer/gstreamerptr.hpp"
|
||||
|
||||
#include <opencv2/ts.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, GetElsByFactoryNameUnitTest)
|
||||
{
|
||||
auto comparator = [](GstElement* element, const std::string& factoryName) {
|
||||
cv::gapi::wip::gst::GStreamerPtr<gchar> name(
|
||||
gst_object_get_name(GST_OBJECT(gst_element_get_factory(element))));
|
||||
return name && (0 == strcmp(name, factoryName.c_str()));
|
||||
};
|
||||
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
auto videotestsrcs = pipelineFacade.getElementsByFactoryName("videotestsrc");
|
||||
EXPECT_EQ(2u, videotestsrcs.size());
|
||||
for (auto&& src : videotestsrcs) {
|
||||
EXPECT_TRUE(comparator(src, "videotestsrc"));
|
||||
}
|
||||
|
||||
auto appsinks = pipelineFacade.getElementsByFactoryName("appsink");
|
||||
EXPECT_EQ(2u, appsinks.size());
|
||||
for (auto&& sink : appsinks) {
|
||||
EXPECT_TRUE(comparator(sink, "appsink"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, GetElByNameUnitTest)
|
||||
{
|
||||
auto comparator = [](GstElement* element, const std::string& elementName) {
|
||||
cv::gapi::wip::gst::GStreamerPtr<gchar> name(gst_element_get_name(element));
|
||||
return name && (0 == strcmp(name, elementName.c_str()));
|
||||
};
|
||||
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
auto appsink1 = pipelineFacade.getElementByName("sink1");
|
||||
GAPI_Assert(appsink1 != nullptr);
|
||||
EXPECT_TRUE(comparator(appsink1, "sink1"));
|
||||
auto appsink2 = pipelineFacade.getElementByName("sink2");
|
||||
GAPI_Assert(appsink2 != nullptr);
|
||||
EXPECT_TRUE(comparator(appsink2, "sink2"));
|
||||
}
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, CompletePrerollUnitTest)
|
||||
{
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
auto appsink = pipelineFacade.getElementByName("sink1");
|
||||
pipelineFacade.completePreroll();
|
||||
|
||||
cv::gapi::wip::gst::GStreamerPtr<GstSample> prerollSample(
|
||||
#if GST_VERSION_MINOR >= 10
|
||||
gst_app_sink_try_pull_preroll(GST_APP_SINK(appsink), 5 * GST_SECOND)
|
||||
#else // GST_VERSION_MINOR < 10
|
||||
// TODO: This function may cause hang with some pipelines, need to check whether these
|
||||
// pipelines are really wrong or not?
|
||||
gst_app_sink_pull_preroll(GST_APP_SINK(appsink))
|
||||
#endif // GST_VERSION_MINOR >= 10
|
||||
);
|
||||
GAPI_Assert(prerollSample != nullptr);
|
||||
}
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, PlayUnitTest)
|
||||
{
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
auto appsink = pipelineFacade.getElementByName("sink2");
|
||||
|
||||
pipelineFacade.play();
|
||||
|
||||
cv::gapi::wip::gst::PipelineState state;
|
||||
GstStateChangeReturn status =
|
||||
gst_element_get_state(appsink, &state.current, &state.pending, 5 * GST_SECOND);
|
||||
EXPECT_EQ(GST_STATE_CHANGE_SUCCESS, status);
|
||||
EXPECT_EQ(GST_STATE_PLAYING, state.current);
|
||||
EXPECT_EQ(GST_STATE_VOID_PENDING, state.pending);
|
||||
}
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, IsPlayingUnitTest)
|
||||
{
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
EXPECT_FALSE(pipelineFacade.isPlaying());
|
||||
pipelineFacade.play();
|
||||
EXPECT_TRUE(pipelineFacade.isPlaying());
|
||||
}
|
||||
|
||||
TEST(GStreamerPipelineFacadeTest, MTSafetyUnitTest)
|
||||
{
|
||||
cv::gapi::wip::gst::GStreamerPipelineFacade
|
||||
pipelineFacade("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
auto prerollRoutine = [&pipelineFacade](){ pipelineFacade.completePreroll(); };
|
||||
auto playRoutine = [&pipelineFacade](){ pipelineFacade.play(); };
|
||||
auto isPlayingRoutine = [&pipelineFacade](){ pipelineFacade.isPlaying(); };
|
||||
|
||||
using f = std::function<void()>;
|
||||
|
||||
auto routinesLauncher = [](const f& r1, const f& r2, const f& r3) {
|
||||
std::vector<f> routines { r1, r2, r3 };
|
||||
std::vector<std::thread> threads { };
|
||||
|
||||
for (auto&& r : routines) {
|
||||
threads.emplace_back(r);
|
||||
}
|
||||
|
||||
for (auto&& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
};
|
||||
|
||||
routinesLauncher(prerollRoutine, playRoutine, isPlayingRoutine);
|
||||
routinesLauncher(prerollRoutine, isPlayingRoutine, playRoutine);
|
||||
routinesLauncher(isPlayingRoutine, prerollRoutine, playRoutine);
|
||||
routinesLauncher(playRoutine, prerollRoutine, isPlayingRoutine);
|
||||
routinesLauncher(playRoutine, isPlayingRoutine, prerollRoutine);
|
||||
routinesLauncher(isPlayingRoutine, playRoutine, prerollRoutine);
|
||||
|
||||
EXPECT_TRUE(true);
|
||||
}
|
||||
} // namespace opencv_test
|
||||
|
||||
#endif // HAVE_GSTREAMER
|
||||
@@ -0,0 +1,563 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
#include "../test/common/gapi_tests_common.hpp"
|
||||
|
||||
#include <opencv2/gapi/streaming/gstreamer/gstreamerpipeline.hpp>
|
||||
#include <opencv2/gapi/streaming/gstreamer/gstreamersource.hpp>
|
||||
#include <opencv2/gapi/core.hpp>
|
||||
#include <opencv2/gapi/cpu/core.hpp>
|
||||
#include <opencv2/gapi/streaming/meta.hpp>
|
||||
#include <opencv2/gapi/streaming/format.hpp>
|
||||
|
||||
#include <opencv2/gapi/gkernel.hpp>
|
||||
#include <opencv2/gapi/cpu/gcpukernel.hpp>
|
||||
#include <opencv2/gapi/gcomputation.hpp>
|
||||
|
||||
#include <opencv2/ts.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
||||
struct GStreamerSourceTest : public TestWithParam<std::tuple<std::string, cv::Size, std::size_t>>
|
||||
{ };
|
||||
|
||||
|
||||
TEST_P(GStreamerSourceTest, AccuracyTest)
|
||||
{
|
||||
std::string pipeline;
|
||||
cv::Size expectedFrameSize;
|
||||
std::size_t streamLength { };
|
||||
std::tie(pipeline, expectedFrameSize, streamLength) = GetParam();
|
||||
|
||||
// Graph declaration:
|
||||
cv::GMat in;
|
||||
auto out = cv::gapi::copy(in);
|
||||
cv::GComputation c(cv::GIn(in), cv::GOut(out));
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
auto ccomp = c.compileStreaming();
|
||||
|
||||
EXPECT_TRUE(ccomp);
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
// GStreamer streaming source configuration:
|
||||
ccomp.setSource<cv::gapi::wip::GStreamerSource>(pipeline);
|
||||
|
||||
// Start of streaming:
|
||||
ccomp.start();
|
||||
EXPECT_TRUE(ccomp.running());
|
||||
|
||||
// Streaming - pulling of frames until the end:
|
||||
cv::Mat in_mat_gapi;
|
||||
|
||||
EXPECT_TRUE(ccomp.pull(cv::gout(in_mat_gapi)));
|
||||
EXPECT_TRUE(!in_mat_gapi.empty());
|
||||
EXPECT_EQ(expectedFrameSize, in_mat_gapi.size());
|
||||
EXPECT_EQ(CV_8UC3, in_mat_gapi.type());
|
||||
|
||||
std::size_t framesCount = 1UL;
|
||||
while (ccomp.pull(cv::gout(in_mat_gapi))) {
|
||||
EXPECT_TRUE(!in_mat_gapi.empty());
|
||||
EXPECT_EQ(expectedFrameSize, in_mat_gapi.size());
|
||||
EXPECT_EQ(CV_8UC3, in_mat_gapi.type());
|
||||
|
||||
framesCount++;
|
||||
}
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
ccomp.stop();
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
EXPECT_EQ(streamLength, framesCount);
|
||||
}
|
||||
|
||||
TEST_P(GStreamerSourceTest, TimestampsTest)
|
||||
{
|
||||
std::string pipeline;
|
||||
std::size_t streamLength { };
|
||||
std::tie(pipeline, std::ignore, streamLength) = GetParam();
|
||||
|
||||
// Graph declaration:
|
||||
cv::GMat in;
|
||||
cv::GMat copied = cv::gapi::copy(in);
|
||||
cv::GOpaque<int64_t> outId = cv::gapi::streaming::seq_id(copied);
|
||||
cv::GOpaque<int64_t> outTs = cv::gapi::streaming::timestamp(copied);
|
||||
cv::GComputation c(cv::GIn(in), cv::GOut(outId, outTs));
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
auto ccomp = c.compileStreaming();
|
||||
|
||||
EXPECT_TRUE(ccomp);
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
// GStreamer streaming source configuration:
|
||||
ccomp.setSource<cv::gapi::wip::GStreamerSource>(pipeline);
|
||||
|
||||
// Start of streaming:
|
||||
ccomp.start();
|
||||
EXPECT_TRUE(ccomp.running());
|
||||
|
||||
// Streaming - pulling of frames until the end:
|
||||
int64_t seqId;
|
||||
int64_t timestamp;
|
||||
|
||||
std::vector<int64_t> allSeqIds;
|
||||
std::vector<int64_t> allTimestamps;
|
||||
|
||||
while (ccomp.pull(cv::gout(seqId, timestamp))) {
|
||||
allSeqIds.push_back(seqId);
|
||||
allTimestamps.push_back(timestamp);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
ccomp.stop();
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
EXPECT_EQ(0L, allSeqIds.front());
|
||||
EXPECT_EQ(int64_t(streamLength) - 1, allSeqIds.back());
|
||||
EXPECT_EQ(streamLength, allSeqIds.size());
|
||||
EXPECT_TRUE(std::is_sorted(allSeqIds.begin(), allSeqIds.end()));
|
||||
EXPECT_EQ(allSeqIds.size(), std::set<int64_t>(allSeqIds.begin(), allSeqIds.end()).size());
|
||||
|
||||
EXPECT_EQ(streamLength, allTimestamps.size());
|
||||
EXPECT_TRUE(std::is_sorted(allTimestamps.begin(), allTimestamps.end()));
|
||||
}
|
||||
|
||||
G_TYPED_KERNEL(GGstFrameCopyToNV12, <std::tuple<cv::GMat,cv::GMat>(GFrame)>,
|
||||
"org.opencv.test.gstframe_copy_to_nv12")
|
||||
{
|
||||
static std::tuple<GMatDesc, GMatDesc> outMeta(GFrameDesc desc) {
|
||||
GMatDesc y { CV_8U, 1, desc.size, false };
|
||||
GMatDesc uv { CV_8U, 2, desc.size / 2, false };
|
||||
|
||||
return std::make_tuple(y, uv);
|
||||
}
|
||||
};
|
||||
|
||||
G_TYPED_KERNEL(GGstFrameCopyToGRAY8, <cv::GMat(GFrame)>,
|
||||
"org.opencv.test.gstframe_copy_to_gray8")
|
||||
{
|
||||
static GMatDesc outMeta(GFrameDesc desc) {
|
||||
GMatDesc y{ CV_8U, 1, desc.size, false };
|
||||
return y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GAPI_OCV_KERNEL(GOCVGstFrameCopyToNV12, GGstFrameCopyToNV12)
|
||||
{
|
||||
static void run(const cv::MediaFrame& in, cv::Mat& y, cv::Mat& uv)
|
||||
{
|
||||
auto view = in.access(cv::MediaFrame::Access::R);
|
||||
cv::Mat ly(y.size(), y.type(), view.ptr[0], view.stride[0]);
|
||||
cv::Mat luv(uv.size(), uv.type(), view.ptr[1], view.stride[1]);
|
||||
|
||||
ly.copyTo(y);
|
||||
luv.copyTo(uv);
|
||||
}
|
||||
};
|
||||
|
||||
GAPI_OCV_KERNEL(GOCVGstFrameCopyToGRAY8, GGstFrameCopyToGRAY8)
|
||||
{
|
||||
static void run(const cv::MediaFrame & in, cv::Mat & y)
|
||||
{
|
||||
auto view = in.access(cv::MediaFrame::Access::R);
|
||||
cv::Mat ly(y.size(), y.type(), view.ptr[0], view.stride[0]);
|
||||
ly.copyTo(y);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_P(GStreamerSourceTest, GFrameTest)
|
||||
{
|
||||
std::string pipeline;
|
||||
cv::Size expectedFrameSize;
|
||||
std::size_t streamLength { };
|
||||
bool isNV12 = false;
|
||||
std::tie(pipeline, expectedFrameSize, streamLength) = GetParam();
|
||||
|
||||
//Check if pipline string contains NV12 sub-string
|
||||
if (pipeline.find("NV12") != std::string::npos) {
|
||||
isNV12 = true;
|
||||
}
|
||||
|
||||
// Graph declaration:
|
||||
cv::GFrame in;
|
||||
cv::GMat copiedY, copiedUV;
|
||||
if (isNV12) {
|
||||
std::tie(copiedY, copiedUV) = GGstFrameCopyToNV12::on(in);
|
||||
}
|
||||
else {
|
||||
copiedY = GGstFrameCopyToGRAY8::on(in);
|
||||
}
|
||||
|
||||
cv::GComputation c(cv::GIn(in), isNV12 ? cv::GOut(copiedY, copiedUV) : cv::GOut(copiedY));
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
cv::GStreamingCompiled ccomp;
|
||||
if (isNV12) {
|
||||
ccomp = c.compileStreaming(cv::compile_args(cv::gapi::kernels<GOCVGstFrameCopyToNV12>()));
|
||||
} else {
|
||||
ccomp = c.compileStreaming(cv::compile_args(cv::gapi::kernels<GOCVGstFrameCopyToGRAY8>()));
|
||||
}
|
||||
|
||||
|
||||
EXPECT_TRUE(ccomp);
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
// GStreamer streaming source configuration:
|
||||
ccomp.setSource<cv::gapi::wip::GStreamerSource>
|
||||
(pipeline, cv::gapi::wip::GStreamerSource::OutputType::FRAME);
|
||||
|
||||
// Start of streaming:
|
||||
ccomp.start();
|
||||
EXPECT_TRUE(ccomp.running());
|
||||
|
||||
// Streaming - pulling of frames until the end:
|
||||
cv::Mat y_mat, uv_mat;
|
||||
|
||||
EXPECT_TRUE(isNV12 ? ccomp.pull(cv::gout(y_mat, uv_mat)) : ccomp.pull(cv::gout(y_mat)));
|
||||
EXPECT_TRUE(!y_mat.empty());
|
||||
if (isNV12) {
|
||||
EXPECT_TRUE(!uv_mat.empty());
|
||||
}
|
||||
|
||||
cv::Size expectedYSize = expectedFrameSize;
|
||||
cv::Size expectedUVSize = expectedFrameSize / 2;
|
||||
|
||||
EXPECT_EQ(expectedYSize, y_mat.size());
|
||||
if (isNV12) {
|
||||
EXPECT_EQ(expectedUVSize, uv_mat.size());
|
||||
}
|
||||
|
||||
EXPECT_EQ(CV_8UC1, y_mat.type());
|
||||
if (isNV12) {
|
||||
EXPECT_EQ(CV_8UC2, uv_mat.type());
|
||||
}
|
||||
|
||||
std::size_t framesCount = 1UL;
|
||||
while (isNV12 ? ccomp.pull(cv::gout(y_mat, uv_mat)) : ccomp.pull(cv::gout(y_mat))) {
|
||||
EXPECT_TRUE(!y_mat.empty());
|
||||
if (isNV12) {
|
||||
EXPECT_TRUE(!uv_mat.empty());
|
||||
}
|
||||
|
||||
EXPECT_EQ(expectedYSize, y_mat.size());
|
||||
if (isNV12) {
|
||||
EXPECT_EQ(expectedUVSize, uv_mat.size());
|
||||
}
|
||||
|
||||
EXPECT_EQ(CV_8UC1, y_mat.type());
|
||||
if (isNV12) {
|
||||
EXPECT_EQ(CV_8UC2, uv_mat.type());
|
||||
}
|
||||
|
||||
framesCount++;
|
||||
}
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
ccomp.stop();
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
EXPECT_EQ(streamLength, framesCount);
|
||||
}
|
||||
|
||||
|
||||
// FIXME: Need to launch with sudo. May be infrastructure problems.
|
||||
// TODO: It is needed to add tests for streaming from native KMB camera: kmbcamsrc
|
||||
// GStreamer element.
|
||||
INSTANTIATE_TEST_CASE_P(CameraEmulatingPipeline, GStreamerSourceTest,
|
||||
Combine(Values("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,format=NV12,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink",
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,format=GRAY8,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink"),
|
||||
Values(cv::Size(1920, 1080)),
|
||||
Values(10UL)));
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FileEmulatingPipeline, GStreamerSourceTest,
|
||||
Combine(Values("videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,format=NV12,width=640,height=420,framerate=3/1 ! "
|
||||
"appsink",
|
||||
"videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,format=GRAY8,width=640,height=420,framerate=3/1 ! "
|
||||
"appsink"),
|
||||
Values(cv::Size(640, 420)),
|
||||
Values(10UL)));
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MultipleLiveSources, GStreamerSourceTest,
|
||||
Combine(Values("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videoscale ! video/x-raw,format=NV12,width=1280,height=720 ! appsink "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"fakesink",
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videoscale ! video/x-raw,format=GRAY8,width=1280,height=720 ! appsink "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"fakesink"),
|
||||
Values(cv::Size(1280, 720)),
|
||||
Values(10UL)));
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MultipleNotLiveSources, GStreamerSourceTest,
|
||||
Combine(Values("videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"videoscale ! video/x-raw,format=NV12,width=1280,height=720 ! appsink "
|
||||
"videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"fakesink",
|
||||
"videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"videoscale ! video/x-raw,format=GRAY8,width=1280,height=720 ! appsink "
|
||||
"videotestsrc pattern=colors num-buffers=10 ! "
|
||||
"fakesink"),
|
||||
Values(cv::Size(1280, 720)),
|
||||
Values(10UL)));
|
||||
|
||||
|
||||
TEST(GStreamerMultiSourceSmokeTest, Test)
|
||||
{
|
||||
// Graph declaration:
|
||||
cv::GMat in1, in2;
|
||||
auto out = cv::gapi::add(in1, in2);
|
||||
cv::GComputation c(cv::GIn(in1, in2), cv::GOut(out));
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
auto ccomp = c.compileStreaming();
|
||||
|
||||
EXPECT_TRUE(ccomp);
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
|
||||
cv::gapi::wip::GStreamerPipeline
|
||||
pipeline("videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink1 "
|
||||
"videotestsrc is-live=true pattern=colors num-buffers=10 ! "
|
||||
"videorate ! videoscale ! "
|
||||
"video/x-raw,width=1920,height=1080,framerate=3/1 ! "
|
||||
"appsink name=sink2");
|
||||
|
||||
// GStreamer streaming sources configuration:
|
||||
auto src1 = pipeline.getStreamingSource("sink1");
|
||||
auto src2 = pipeline.getStreamingSource("sink2");
|
||||
|
||||
ccomp.setSource(cv::gin(src1, src2));
|
||||
|
||||
// Start of streaming:
|
||||
ccomp.start();
|
||||
EXPECT_TRUE(ccomp.running());
|
||||
|
||||
// Streaming - pulling of frames until the end:
|
||||
cv::Mat in_mat_gapi;
|
||||
|
||||
EXPECT_TRUE(ccomp.pull(cv::gout(in_mat_gapi)));
|
||||
EXPECT_TRUE(!in_mat_gapi.empty());
|
||||
EXPECT_EQ(CV_8UC3, in_mat_gapi.type());
|
||||
|
||||
while (ccomp.pull(cv::gout(in_mat_gapi))) {
|
||||
EXPECT_TRUE(!in_mat_gapi.empty());
|
||||
EXPECT_EQ(CV_8UC3, in_mat_gapi.type());
|
||||
}
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
ccomp.stop();
|
||||
|
||||
EXPECT_FALSE(ccomp.running());
|
||||
}
|
||||
|
||||
struct GStreamerMultiSourceTestNV12 :
|
||||
public TestWithParam<std::tuple<cv::GComputation, cv::gapi::wip::GStreamerSource::OutputType>>
|
||||
{ };
|
||||
|
||||
TEST_P(GStreamerMultiSourceTestNV12, ImageDataTest)
|
||||
{
|
||||
std::string pathToLeftIm = findDataFile("cv/stereomatching/datasets/tsukuba/im6.png");
|
||||
std::string pathToRightIm = findDataFile("cv/stereomatching/datasets/tsukuba/im2.png");
|
||||
|
||||
std::string pipelineToReadImage("filesrc location=LOC ! pngdec ! videoconvert ! "
|
||||
"videoscale ! video/x-raw,format=NV12 ! appsink");
|
||||
|
||||
cv::gapi::wip::GStreamerSource leftImageProvider(
|
||||
std::regex_replace(pipelineToReadImage, std::regex("LOC"), pathToLeftIm));
|
||||
cv::gapi::wip::GStreamerSource rightImageProvider(
|
||||
std::regex_replace(pipelineToReadImage, std::regex("LOC"), pathToRightIm));
|
||||
|
||||
cv::gapi::wip::Data leftImData, rightImData;
|
||||
leftImageProvider.pull(leftImData);
|
||||
rightImageProvider.pull(rightImData);
|
||||
|
||||
cv::Mat leftRefMat = cv::util::get<cv::Mat>(leftImData);
|
||||
cv::Mat rightRefMat = cv::util::get<cv::Mat>(rightImData);
|
||||
|
||||
// Retrieve test parameters:
|
||||
std::tuple<cv::GComputation, cv::gapi::wip::GStreamerSource::OutputType> params = GetParam();
|
||||
cv::GComputation extractImage = std::move(std::get<0>(params));
|
||||
cv::gapi::wip::GStreamerSource::OutputType outputType = std::get<1>(params);
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
auto compiled =
|
||||
extractImage.compileStreaming();
|
||||
|
||||
EXPECT_TRUE(compiled);
|
||||
EXPECT_FALSE(compiled.running());
|
||||
|
||||
cv::gapi::wip::GStreamerPipeline
|
||||
pipeline(std::string("multifilesrc location=" + pathToLeftIm + " index=0 loop=true ! "
|
||||
"pngdec ! videoconvert ! videoscale ! video/x-raw,format=NV12 ! "
|
||||
"appsink name=sink1 ") +
|
||||
std::string("multifilesrc location=" + pathToRightIm + " index=0 loop=true ! "
|
||||
"pngdec ! videoconvert ! videoscale ! video/x-raw,format=NV12 ! "
|
||||
"appsink name=sink2"));
|
||||
|
||||
// GStreamer streaming sources configuration:
|
||||
auto src1 = pipeline.getStreamingSource("sink1", outputType);
|
||||
auto src2 = pipeline.getStreamingSource("sink2", outputType);
|
||||
|
||||
compiled.setSource(cv::gin(src1, src2));
|
||||
|
||||
// Start of streaming:
|
||||
compiled.start();
|
||||
EXPECT_TRUE(compiled.running());
|
||||
|
||||
// Streaming - pulling of frames:
|
||||
cv::Mat in_mat1, in_mat2;
|
||||
|
||||
std::size_t counter { }, limit { 10 };
|
||||
while(compiled.pull(cv::gout(in_mat1, in_mat2)) && (counter < limit)) {
|
||||
EXPECT_EQ(0, cv::norm(in_mat1, leftRefMat, cv::NORM_INF));
|
||||
EXPECT_EQ(0, cv::norm(in_mat2, rightRefMat, cv::NORM_INF));
|
||||
++counter;
|
||||
}
|
||||
|
||||
compiled.stop();
|
||||
|
||||
EXPECT_FALSE(compiled.running());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(GStreamerMultiSourceViaGMatsTest, GStreamerMultiSourceTestNV12,
|
||||
Combine(Values(cv::GComputation([]()
|
||||
{
|
||||
cv::GMat in1, in2;
|
||||
return cv::GComputation(cv::GIn(in1, in2),
|
||||
cv::GOut(cv::gapi::copy(in1),
|
||||
cv::gapi::copy(in2)));
|
||||
})),
|
||||
Values(cv::gapi::wip::GStreamerSource::OutputType::MAT)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(GStreamerMultiSourceViaGFramesTest, GStreamerMultiSourceTestNV12,
|
||||
Combine(Values(cv::GComputation([]()
|
||||
{
|
||||
cv::GFrame in1, in2;
|
||||
return cv::GComputation(cv::GIn(in1, in2),
|
||||
cv::GOut(cv::gapi::streaming::BGR(in1),
|
||||
cv::gapi::streaming::BGR(in2)));
|
||||
})),
|
||||
Values(cv::gapi::wip::GStreamerSource::OutputType::FRAME)));
|
||||
|
||||
struct GStreamerMultiSourceTestGRAY8 :
|
||||
public TestWithParam<std::tuple<cv::GComputation, cv::gapi::wip::GStreamerSource::OutputType>>
|
||||
{ };
|
||||
|
||||
TEST_P(GStreamerMultiSourceTestGRAY8, ImageDataTest)
|
||||
{
|
||||
std::string pathToLeftIm = findDataFile("cv/stereomatching/datasets/tsukuba/im6.png");
|
||||
std::string pathToRightIm = findDataFile("cv/stereomatching/datasets/tsukuba/im2.png");
|
||||
|
||||
std::string pipelineToReadImage("filesrc location=LOC ! pngdec ! videoconvert ! "
|
||||
"videoscale ! video/x-raw,format=GRAY8 ! appsink");
|
||||
|
||||
cv::gapi::wip::GStreamerSource leftImageProvider(
|
||||
std::regex_replace(pipelineToReadImage, std::regex("LOC"), pathToLeftIm));
|
||||
cv::gapi::wip::GStreamerSource rightImageProvider(
|
||||
std::regex_replace(pipelineToReadImage, std::regex("LOC"), pathToRightIm));
|
||||
|
||||
cv::gapi::wip::Data leftImData, rightImData;
|
||||
leftImageProvider.pull(leftImData);
|
||||
rightImageProvider.pull(rightImData);
|
||||
|
||||
cv::Mat leftRefMat = cv::util::get<cv::Mat>(leftImData);
|
||||
cv::Mat rightRefMat = cv::util::get<cv::Mat>(rightImData);
|
||||
|
||||
// Retrieve test parameters:
|
||||
std::tuple<cv::GComputation, cv::gapi::wip::GStreamerSource::OutputType> params = GetParam();
|
||||
cv::GComputation extractImage = std::move(std::get<0>(params));
|
||||
cv::gapi::wip::GStreamerSource::OutputType outputType = std::get<1>(params);
|
||||
|
||||
// Graph compilation for streaming mode:
|
||||
auto compiled =
|
||||
extractImage.compileStreaming();
|
||||
|
||||
EXPECT_TRUE(compiled);
|
||||
EXPECT_FALSE(compiled.running());
|
||||
|
||||
cv::gapi::wip::GStreamerPipeline
|
||||
pipeline(std::string("multifilesrc location=" + pathToLeftIm + " index=0 loop=true ! "
|
||||
"pngdec ! videoconvert ! videoscale ! video/x-raw,format=GRAY8 ! "
|
||||
"appsink name=sink1 ") +
|
||||
std::string("multifilesrc location=" + pathToRightIm + " index=0 loop=true ! "
|
||||
"pngdec ! videoconvert ! videoscale ! video/x-raw,format=GRAY8 ! "
|
||||
"appsink name=sink2"));
|
||||
|
||||
// GStreamer streaming sources configuration:
|
||||
auto src1 = pipeline.getStreamingSource("sink1", outputType);
|
||||
auto src2 = pipeline.getStreamingSource("sink2", outputType);
|
||||
|
||||
compiled.setSource(cv::gin(src1, src2));
|
||||
|
||||
// Start of streaming:
|
||||
compiled.start();
|
||||
EXPECT_TRUE(compiled.running());
|
||||
|
||||
// Streaming - pulling of frames:
|
||||
cv::Mat in_mat1, in_mat2;
|
||||
|
||||
std::size_t counter { }, limit { 10 };
|
||||
while(compiled.pull(cv::gout(in_mat1, in_mat2)) && (counter < limit)) {
|
||||
EXPECT_EQ(0, cv::norm(in_mat1, leftRefMat, cv::NORM_INF));
|
||||
EXPECT_EQ(0, cv::norm(in_mat2, rightRefMat, cv::NORM_INF));
|
||||
++counter;
|
||||
}
|
||||
|
||||
compiled.stop();
|
||||
|
||||
EXPECT_FALSE(compiled.running());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(GStreamerMultiSourceViaGMatsTest, GStreamerMultiSourceTestGRAY8,
|
||||
Combine(Values(cv::GComputation([]()
|
||||
{
|
||||
cv::GMat in1, in2;
|
||||
return cv::GComputation(cv::GIn(in1, in2),
|
||||
cv::GOut(cv::gapi::copy(in1),
|
||||
cv::gapi::copy(in2)));
|
||||
})),
|
||||
Values(cv::gapi::wip::GStreamerSource::OutputType::MAT)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(GStreamerMultiSourceViaGFramesTest, GStreamerMultiSourceTestGRAY8,
|
||||
Combine(Values(cv::GComputation([]()
|
||||
{
|
||||
cv::GFrame in1, in2;
|
||||
return cv::GComputation(cv::GIn(in1, in2),
|
||||
cv::GOut(cv::gapi::streaming::BGR(in1),
|
||||
cv::gapi::streaming::BGR(in2)));
|
||||
})),
|
||||
Values(cv::gapi::wip::GStreamerSource::OutputType::FRAME)));
|
||||
|
||||
} // namespace opencv_test
|
||||
|
||||
#endif // HAVE_GSTREAMER
|
||||
@@ -0,0 +1,127 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2023 Intel Corporation
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include <opencv2/gapi/gstreaming.hpp>
|
||||
#include <opencv2/gapi/streaming/queue_source.hpp>
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
|
||||
TEST(GAPI_Streaming_Queue_Source, SmokeTest) {
|
||||
// This is more like an example on G-API Queue Source
|
||||
|
||||
cv::GMat in;
|
||||
cv::GMat out = in + 1;
|
||||
cv::GStreamingCompiled comp = cv::GComputation(in, out).compileStreaming();
|
||||
|
||||
// Queue source needs to know format information to maintain contracts
|
||||
auto src = std::make_shared<cv::gapi::wip::QueueSource<cv::Mat> >
|
||||
(cv::GMatDesc{CV_8U, 1, cv::Size{128, 128}});
|
||||
|
||||
comp.setSource(cv::gin(src->ptr()));
|
||||
comp.start();
|
||||
|
||||
// It is perfectly legal to start a pipeline at this point - the source was passed.
|
||||
// Now we can push data through the source and get the pipeline results.
|
||||
|
||||
cv::Mat eye = cv::Mat::eye(cv::Size{128, 128}, CV_8UC1);
|
||||
src->push(eye); // Push I (identity matrix)
|
||||
src->push(eye*2); // Push I*2
|
||||
|
||||
// Now its time to pop. The data could be already processed at this point.
|
||||
// Note the queue source queues are unbounded to avoid deadlocks
|
||||
|
||||
cv::Mat result;
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(eye + 1, result, NORM_INF));
|
||||
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(eye*2 + 1, result, NORM_INF));
|
||||
}
|
||||
|
||||
TEST(GAPI_Streaming_Queue_Source, Mixed) {
|
||||
// Mixing a regular "live" source (which runs on its own) with a
|
||||
// manually controlled queue source may make a little sense, but
|
||||
// is perfectly legal and possible.
|
||||
|
||||
cv::GMat in1;
|
||||
cv::GMat in2;
|
||||
cv::GMat out = in2 - in1;
|
||||
cv::GStreamingCompiled comp = cv::GComputation(in1, in2, out).compileStreaming();
|
||||
|
||||
// Queue source needs to know format information to maintain contracts
|
||||
auto src1 = std::make_shared<cv::gapi::wip::QueueSource<cv::Mat> >
|
||||
(cv::GMatDesc{CV_8U, 3, cv::Size{768, 576}});
|
||||
|
||||
std::shared_ptr<cv::gapi::wip::IStreamSource> src2;
|
||||
auto path = findDataFile("cv/video/768x576.avi");
|
||||
try {
|
||||
src2 = cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path);
|
||||
} catch(...) {
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
}
|
||||
|
||||
comp.setSource(cv::gin(src1->ptr(), src2)); // FIXME: quite inconsistent
|
||||
comp.start();
|
||||
|
||||
cv::Mat eye = cv::Mat::eye(cv::Size{768, 576}, CV_8UC3);
|
||||
src1->push(eye); // Push I (identity matrix)
|
||||
src1->push(eye); // Push I (again)
|
||||
|
||||
cv::Mat ref, result;
|
||||
cv::VideoCapture cap(path);
|
||||
|
||||
cap >> ref;
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(ref - eye, result, NORM_INF));
|
||||
|
||||
cap >> ref;
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(ref - eye, result, NORM_INF));
|
||||
}
|
||||
|
||||
TEST(GAPI_Streaming_Queue_Input, SmokeTest) {
|
||||
|
||||
// Queue Input: a tiny wrapper atop of multiple queue sources.
|
||||
// Allows users to pass all input data at once.
|
||||
|
||||
cv::GMat in1;
|
||||
cv::GScalar in2;
|
||||
cv::GMat out = in1 + in2;
|
||||
cv::GStreamingCompiled comp = cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
|
||||
.compileStreaming();
|
||||
|
||||
// FIXME: This API is too raw
|
||||
cv::gapi::wip::QueueInput input({
|
||||
cv::GMetaArg{ cv::GMatDesc{CV_8U, 1, cv::Size{64,64} } },
|
||||
cv::GMetaArg{ cv::empty_scalar_desc() }
|
||||
});
|
||||
comp.setSource(input); // Implicit conversion allows it to be passed as-is.
|
||||
comp.start();
|
||||
|
||||
// Push data via queue input
|
||||
cv::Mat eye = cv::Mat::eye(cv::Size{64, 64}, CV_8UC1);
|
||||
input.push(cv::gin(eye, cv::Scalar(1)));
|
||||
input.push(cv::gin(eye, cv::Scalar(2)));
|
||||
input.push(cv::gin(eye, cv::Scalar(3)));
|
||||
|
||||
// Pop data and validate
|
||||
cv::Mat result;
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(eye+1, result, NORM_INF));
|
||||
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(eye+2, result, NORM_INF));
|
||||
|
||||
ASSERT_TRUE(comp.pull(cv::gout(result)));
|
||||
EXPECT_EQ(0, cvtest::norm(eye+3, result, NORM_INF));
|
||||
}
|
||||
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,220 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
#include <opencv2/gapi/core.hpp>
|
||||
#include <opencv2/gapi/fluid/imgproc.hpp>
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
#include <opencv2/gapi/streaming/sync.hpp>
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
using ts_t = int64_t;
|
||||
using ts_vec = std::vector<ts_t>;
|
||||
using cv::gapi::streaming::sync_policy;
|
||||
|
||||
ts_t calcLeastCommonMultiple(const ts_vec& values) {
|
||||
ts_t res = *std::max_element(values.begin(), values.end());
|
||||
auto isDivisor = [&](ts_t v) { return res % v == 0; };
|
||||
while(!std::all_of(values.begin(), values.end(), isDivisor)) {
|
||||
res++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct TimestampGenerationParams {
|
||||
const ts_vec frame_times;
|
||||
sync_policy policy;
|
||||
ts_t end_time;
|
||||
TimestampGenerationParams(const ts_vec& ft, sync_policy sp, ts_t et = 25)
|
||||
: frame_times(ft), policy(sp), end_time(et) {
|
||||
}
|
||||
};
|
||||
|
||||
class MultiFrameSource {
|
||||
class SingleSource : public cv::gapi::wip::IStreamSource {
|
||||
MultiFrameSource& m_source;
|
||||
std::size_t m_idx;
|
||||
public:
|
||||
SingleSource(MultiFrameSource& s, std::size_t idx)
|
||||
: m_source(s)
|
||||
, m_idx(idx)
|
||||
{}
|
||||
virtual bool pull(cv::gapi::wip::Data& data) {
|
||||
return m_source.pull(data, m_idx);
|
||||
}
|
||||
virtual GMetaArg descr_of() const { return GMetaArg{m_source.desc()}; }
|
||||
};
|
||||
|
||||
TimestampGenerationParams p;
|
||||
ts_vec m_current_times;
|
||||
cv::Mat m_mat;
|
||||
|
||||
public:
|
||||
MultiFrameSource(const TimestampGenerationParams& params)
|
||||
: p(params)
|
||||
, m_current_times(p.frame_times.size(), 0u)
|
||||
, m_mat(8, 8, CV_8UC1) {
|
||||
}
|
||||
|
||||
bool pull(cv::gapi::wip::Data& data, std::size_t idx) {
|
||||
cv::randn(m_mat, 127, 32);
|
||||
GAPI_Assert(idx < p.frame_times.size());
|
||||
m_current_times[idx] += p.frame_times[idx];
|
||||
if (m_current_times[idx] >= p.end_time) {
|
||||
return false;
|
||||
}
|
||||
data = m_mat.clone();
|
||||
data.meta[cv::gapi::streaming::meta_tag::timestamp] = m_current_times[idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
cv::gapi::wip::IStreamSource::Ptr getSource(std::size_t idx) {
|
||||
return cv::gapi::wip::IStreamSource::Ptr{new SingleSource(*this, idx)};
|
||||
}
|
||||
|
||||
GMatDesc desc() const { return cv::descr_of(m_mat); }
|
||||
};
|
||||
|
||||
class TimestampChecker {
|
||||
TimestampGenerationParams p;
|
||||
ts_t m_synced_time = 0u;
|
||||
ts_t m_synced_frame_time = 0u;
|
||||
public:
|
||||
TimestampChecker(const TimestampGenerationParams& params)
|
||||
: p(params)
|
||||
, m_synced_frame_time(calcLeastCommonMultiple(p.frame_times)) {
|
||||
}
|
||||
|
||||
void checkNext(const ts_vec& timestamps) {
|
||||
if (p.policy == sync_policy::dont_sync) {
|
||||
// don't check timestamps if the policy is dont_sync
|
||||
return;
|
||||
}
|
||||
m_synced_time += m_synced_frame_time;
|
||||
for (const auto& ts : timestamps) {
|
||||
EXPECT_EQ(m_synced_time, ts);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t nFrames() const {
|
||||
auto frame_time = p.policy == sync_policy::dont_sync
|
||||
? *std::max_element(p.frame_times.begin(), p.frame_times.end())
|
||||
: m_synced_frame_time;
|
||||
auto n_frames = p.end_time / frame_time;
|
||||
GAPI_Assert(n_frames > 0u);
|
||||
return (std::size_t)n_frames;
|
||||
}
|
||||
};
|
||||
|
||||
struct TimestampSyncTest : public ::testing::TestWithParam<sync_policy> {
|
||||
void run(cv::GProtoInputArgs&& ins, cv::GProtoOutputArgs&& outs,
|
||||
const ts_vec& frame_times) {
|
||||
auto video_in_n = frame_times.size();
|
||||
GAPI_Assert(video_in_n <= ins.m_args.size());
|
||||
// Assume that all remaining inputs are const
|
||||
auto const_in_n = ins.m_args.size() - video_in_n;
|
||||
auto out_n = outs.m_args.size();
|
||||
auto policy = GetParam();
|
||||
TimestampGenerationParams ts_params(frame_times, policy);
|
||||
MultiFrameSource source(ts_params);
|
||||
|
||||
GRunArgs gins;
|
||||
for (std::size_t i = 0; i < video_in_n; i++) {
|
||||
gins += cv::gin(source.getSource(i));
|
||||
}
|
||||
auto desc = source.desc();
|
||||
cv::Mat const_mat = cv::Mat::eye(desc.size.height,
|
||||
desc.size.width,
|
||||
CV_MAKE_TYPE(desc.depth, desc.chan));
|
||||
for (std::size_t i = 0; i < const_in_n; i++) {
|
||||
gins += cv::gin(const_mat);
|
||||
}
|
||||
ts_vec out_timestamps(out_n);
|
||||
cv::GRunArgsP gouts{};
|
||||
for (auto& t : out_timestamps) {
|
||||
gouts += cv::gout(t);
|
||||
}
|
||||
|
||||
auto pipe = cv::GComputation(std::move(ins), std::move(outs))
|
||||
.compileStreaming(cv::compile_args(policy));
|
||||
|
||||
pipe.setSource(std::move(gins));
|
||||
pipe.start();
|
||||
|
||||
std::size_t frames = 0u;
|
||||
TimestampChecker checker(ts_params);
|
||||
while(pipe.pull(std::move(gouts))) {
|
||||
checker.checkNext(out_timestamps);
|
||||
frames++;
|
||||
}
|
||||
|
||||
EXPECT_EQ(checker.nFrames(), frames);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_P(TimestampSyncTest, Basic)
|
||||
{
|
||||
cv::GMat in1, in2;
|
||||
auto out = cv::gapi::add(in1, in2);
|
||||
auto ts = cv::gapi::streaming::timestamp(out);
|
||||
|
||||
run(cv::GIn(in1, in2), cv::GOut(ts), {1,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ThreeInputs)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto tmp = cv::gapi::add(in1, in2);
|
||||
auto out = cv::gapi::add(tmp, in3);
|
||||
auto ts = cv::gapi::streaming::timestamp(out);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts), {2,4,3});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, TwoOutputs)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,4,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ConstInput)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
}
|
||||
|
||||
TEST_P(TimestampSyncTest, ChangeSource)
|
||||
{
|
||||
cv::GMat in1, in2, in3;
|
||||
auto out1 = cv::gapi::add(in1, in3);
|
||||
auto out2 = cv::gapi::add(in2, in3);
|
||||
auto ts1 = cv::gapi::streaming::timestamp(out1);
|
||||
auto ts2 = cv::gapi::streaming::timestamp(out2);
|
||||
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
run(cv::GIn(in1, in2, in3), cv::GOut(ts1, ts2), {1,2});
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InputSynchronization, TimestampSyncTest,
|
||||
Values(sync_policy::dont_sync,
|
||||
sync_policy::drop));
|
||||
} // namespace opencv_test
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,349 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include "../common/gapi_streaming_tests_common.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
#define private public
|
||||
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
|
||||
#undef private
|
||||
|
||||
#include "streaming/onevpl/accelerators/utils/elastic_barrier.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using cv::gapi::wip::onevpl::SharedLock;
|
||||
|
||||
struct TestBarrier : public cv::gapi::wip::onevpl::elastic_barrier<TestBarrier> {
|
||||
void on_first_in_impl(size_t visitor_id) {
|
||||
|
||||
static std::atomic<int> thread_counter{};
|
||||
thread_counter++;
|
||||
EXPECT_EQ(thread_counter.load(), 1);
|
||||
|
||||
visitors_in.insert(visitor_id);
|
||||
last_visitor_id = visitor_id;
|
||||
|
||||
thread_counter--;
|
||||
EXPECT_EQ(thread_counter.load(), 0);
|
||||
}
|
||||
|
||||
void on_last_out_impl(size_t visitor_id) {
|
||||
|
||||
static std::atomic<int> thread_counter{};
|
||||
thread_counter++;
|
||||
EXPECT_EQ(thread_counter.load(), 1);
|
||||
|
||||
visitors_out.insert(visitor_id);
|
||||
last_visitor_id = visitor_id;
|
||||
|
||||
thread_counter--;
|
||||
EXPECT_EQ(thread_counter.load(), 0);
|
||||
}
|
||||
|
||||
size_t last_visitor_id = 0;
|
||||
std::set<size_t> visitors_in;
|
||||
std::set<size_t> visitors_out;
|
||||
};
|
||||
|
||||
TEST(OneVPL_SharedLock, Create) {
|
||||
SharedLock lock;
|
||||
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, Read_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
const size_t single_thread_read_count = 100;
|
||||
for(size_t i = 0; i < single_thread_read_count; i++) {
|
||||
lock.shared_lock();
|
||||
EXPECT_FALSE(lock.owns());
|
||||
}
|
||||
EXPECT_EQ(lock.shared_counter.load(), single_thread_read_count);
|
||||
|
||||
for(size_t i = 0; i < single_thread_read_count; i++) {
|
||||
lock.unlock_shared();
|
||||
EXPECT_FALSE(lock.owns());
|
||||
}
|
||||
|
||||
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, TryLock_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
EXPECT_TRUE(lock.try_lock());
|
||||
EXPECT_TRUE(lock.owns());
|
||||
|
||||
lock.unlock();
|
||||
EXPECT_FALSE(lock.owns());
|
||||
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, Write_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
lock.lock();
|
||||
EXPECT_TRUE(lock.owns());
|
||||
|
||||
lock.unlock();
|
||||
EXPECT_FALSE(lock.owns());
|
||||
EXPECT_EQ(lock.shared_counter.load(), size_t{0});
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, TryLockTryLock_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
lock.try_lock();
|
||||
EXPECT_FALSE(lock.try_lock());
|
||||
lock.unlock();
|
||||
|
||||
EXPECT_FALSE(lock.owns());
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, ReadTryLock_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
lock.shared_lock();
|
||||
EXPECT_FALSE(lock.owns());
|
||||
EXPECT_FALSE(lock.try_lock());
|
||||
lock.unlock_shared();
|
||||
|
||||
EXPECT_TRUE(lock.try_lock());
|
||||
EXPECT_TRUE(lock.owns());
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, WriteTryLock_SingleThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
lock.lock();
|
||||
EXPECT_TRUE(lock.owns());
|
||||
EXPECT_FALSE(lock.try_lock());
|
||||
lock.unlock();
|
||||
|
||||
EXPECT_TRUE(lock.try_lock());
|
||||
EXPECT_TRUE(lock.owns());
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
|
||||
TEST(OneVPL_SharedLock, Write_MultiThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
std::promise<void> barrier;
|
||||
std::shared_future<void> sync = barrier.get_future();
|
||||
|
||||
static const size_t inc_count = 10000000;
|
||||
size_t shared_value = 0;
|
||||
auto work = [&lock, &shared_value](size_t count) {
|
||||
for (size_t i = 0; i < count; i ++) {
|
||||
lock.lock();
|
||||
shared_value ++;
|
||||
lock.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
std::thread worker_thread([&barrier, sync, work] () {
|
||||
|
||||
std::thread sub_worker([&barrier, work] () {
|
||||
barrier.set_value();
|
||||
work(inc_count);
|
||||
});
|
||||
|
||||
sync.wait();
|
||||
work(inc_count);
|
||||
sub_worker.join();
|
||||
});
|
||||
sync.wait();
|
||||
|
||||
work(inc_count);
|
||||
worker_thread.join();
|
||||
|
||||
EXPECT_EQ(shared_value, inc_count * 3);
|
||||
}
|
||||
|
||||
TEST(OneVPL_SharedLock, ReadWrite_MultiThread)
|
||||
{
|
||||
SharedLock lock;
|
||||
|
||||
std::promise<void> barrier;
|
||||
std::future<void> sync = barrier.get_future();
|
||||
|
||||
static const size_t inc_count = 10000000;
|
||||
size_t shared_value = 0;
|
||||
auto write_work = [&lock, &shared_value](size_t count) {
|
||||
for (size_t i = 0; i < count; i ++) {
|
||||
lock.lock();
|
||||
shared_value ++;
|
||||
lock.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
auto read_work = [&lock, &shared_value](size_t count) {
|
||||
|
||||
auto old_shared_value = shared_value;
|
||||
for (size_t i = 0; i < count; i ++) {
|
||||
lock.shared_lock();
|
||||
EXPECT_TRUE(shared_value >= old_shared_value);
|
||||
old_shared_value = shared_value;
|
||||
lock.unlock_shared();
|
||||
}
|
||||
};
|
||||
|
||||
std::thread writer_thread([&barrier, write_work] () {
|
||||
barrier.set_value();
|
||||
write_work(inc_count);
|
||||
});
|
||||
sync.wait();
|
||||
|
||||
read_work(inc_count);
|
||||
writer_thread.join();
|
||||
|
||||
EXPECT_EQ(shared_value, inc_count);
|
||||
}
|
||||
|
||||
|
||||
TEST(OneVPL_ElasticBarrier, single_thread_visit)
|
||||
{
|
||||
TestBarrier barrier;
|
||||
|
||||
const size_t max_visit_count = 10000;
|
||||
size_t visit_id = 0;
|
||||
for (visit_id = 0; visit_id < max_visit_count; visit_id++) {
|
||||
barrier.visit_in(visit_id);
|
||||
EXPECT_EQ(barrier.visitors_in.size(), size_t{1});
|
||||
}
|
||||
EXPECT_EQ(barrier.last_visitor_id, size_t{0});
|
||||
EXPECT_EQ(barrier.visitors_out.size(), size_t{0});
|
||||
|
||||
for (visit_id = 0; visit_id < max_visit_count; visit_id++) {
|
||||
barrier.visit_out(visit_id);
|
||||
EXPECT_EQ(barrier.visitors_in.size(), size_t{1});
|
||||
}
|
||||
EXPECT_EQ(barrier.last_visitor_id, visit_id - 1);
|
||||
EXPECT_EQ(barrier.visitors_out.size(), size_t{1});
|
||||
}
|
||||
|
||||
|
||||
TEST(OneVPL_ElasticBarrier, multi_thread_visit)
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_VERYLONG);
|
||||
TestBarrier tested_barrier;
|
||||
|
||||
static const size_t max_visit_count = 10000000;
|
||||
std::atomic<size_t> visit_in_wait_counter{};
|
||||
std::promise<void> start_sync_barrier;
|
||||
std::shared_future<void> start_sync = start_sync_barrier.get_future();
|
||||
std::promise<void> phase_sync_barrier;
|
||||
std::shared_future<void> phase_sync = phase_sync_barrier.get_future();
|
||||
|
||||
auto visit_worker_job = [&tested_barrier,
|
||||
&visit_in_wait_counter,
|
||||
start_sync,
|
||||
phase_sync] (size_t worker_id) {
|
||||
|
||||
start_sync.wait();
|
||||
|
||||
// first phase
|
||||
const size_t begin_range = worker_id * max_visit_count;
|
||||
const size_t end_range = (worker_id + 1) * max_visit_count;
|
||||
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
|
||||
tested_barrier.visit_in(visit_id);
|
||||
}
|
||||
|
||||
// notify all worker first phase ready
|
||||
visit_in_wait_counter.fetch_add(1);
|
||||
|
||||
// wait main second phase
|
||||
phase_sync.wait();
|
||||
|
||||
// second phase
|
||||
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
|
||||
tested_barrier.visit_out(visit_id);
|
||||
}
|
||||
};
|
||||
|
||||
auto visit_main_job = [&tested_barrier,
|
||||
&visit_in_wait_counter,
|
||||
&phase_sync_barrier] (size_t total_workers_count,
|
||||
size_t worker_id) {
|
||||
|
||||
const size_t begin_range = worker_id * max_visit_count;
|
||||
const size_t end_range = (worker_id + 1) * max_visit_count;
|
||||
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
|
||||
tested_barrier.visit_in(visit_id);
|
||||
}
|
||||
|
||||
// wait all workers first phase done
|
||||
visit_in_wait_counter.fetch_add(1);
|
||||
while (visit_in_wait_counter.load() != total_workers_count) {
|
||||
std::this_thread::yield();
|
||||
};
|
||||
|
||||
// TEST invariant: last_visitor_id MUST be one from any FIRST worker visitor_id
|
||||
bool one_of_available_ids_matched = false;
|
||||
for (size_t id = 0; id < total_workers_count; id ++) {
|
||||
size_t expected_last_visitor_for_id = id * max_visit_count;
|
||||
one_of_available_ids_matched |=
|
||||
(tested_barrier.last_visitor_id == expected_last_visitor_for_id) ;
|
||||
}
|
||||
EXPECT_TRUE(one_of_available_ids_matched);
|
||||
|
||||
// unblock all workers to work out second phase
|
||||
phase_sync_barrier.set_value();
|
||||
|
||||
// continue second phase
|
||||
for (size_t visit_id = begin_range; visit_id < end_range; visit_id++) {
|
||||
tested_barrier.visit_out(visit_id);
|
||||
}
|
||||
};
|
||||
|
||||
size_t max_worker_count = std::thread::hardware_concurrency();
|
||||
if (max_worker_count < 2) {
|
||||
max_worker_count = 2; // logical 2 threads required at least
|
||||
}
|
||||
std::vector<std::thread> workers;
|
||||
workers.reserve(max_worker_count);
|
||||
for (size_t worker_id = 1; worker_id < max_worker_count; worker_id++) {
|
||||
workers.emplace_back(visit_worker_job, worker_id);
|
||||
}
|
||||
|
||||
// let's go for first phase
|
||||
start_sync_barrier.set_value();
|
||||
|
||||
// utilize main thread as well
|
||||
visit_main_job(max_worker_count, 0);
|
||||
|
||||
// join all threads second phase
|
||||
for (auto& w : workers) {
|
||||
w.join();
|
||||
}
|
||||
|
||||
// TEST invariant: last_visitor_id MUST be one from any LATTER worker visitor_id
|
||||
bool one_of_available_ids_matched = false;
|
||||
for (size_t id = 0; id < max_worker_count; id ++) {
|
||||
one_of_available_ids_matched |=
|
||||
(tested_barrier.last_visitor_id == ((id + 1) * max_visit_count - 1)) ;
|
||||
}
|
||||
EXPECT_TRUE(one_of_available_ids_matched);
|
||||
}
|
||||
}
|
||||
} // opencv_test
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
#ifdef HAVE_ONEVPL
|
||||
|
||||
#include <future>
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include "../common/gapi_tests_common.hpp"
|
||||
#include "streaming/onevpl/data_provider_dispatcher.hpp"
|
||||
#include "streaming/onevpl/file_data_provider.hpp"
|
||||
#include "streaming/onevpl/demux/async_mfp_demux_data_provider.hpp"
|
||||
#include "streaming/onevpl/source_priv.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using source_t = std::string;
|
||||
using dd_valid_t = bool;
|
||||
using demux_valid_t = bool;
|
||||
using dec_valid_t = bool;
|
||||
using array_element_t =
|
||||
std::tuple<source_t, dd_valid_t, demux_valid_t, dec_valid_t>;
|
||||
array_element_t files[] = {
|
||||
array_element_t {"highgui/video/VID00003-20100701-2204.3GP",
|
||||
false, true, false},
|
||||
array_element_t {"highgui/video/VID00003-20100701-2204.avi",
|
||||
false, true, false},
|
||||
array_element_t {"highgui/video/VID00003-20100701-2204.mpg",
|
||||
false, true, false},
|
||||
array_element_t {"highgui/video/VID00003-20100701-2204.wmv",
|
||||
false, true, false},
|
||||
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libaom-av1.mp4",
|
||||
true, true, true},
|
||||
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libvpx-vp9.mp4",
|
||||
true, true, true},
|
||||
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx264.mp4",
|
||||
true, true, true},
|
||||
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4",
|
||||
true, true, true},
|
||||
array_element_t {"highgui/video/sample_322x242_15frames.yuv420p.mjpeg.mp4",
|
||||
/* MFP cannot extract video MJPEG subtype from that */
|
||||
false, false, true},
|
||||
array_element_t {"highgui/video/big_buck_bunny.h264",
|
||||
false, false, false},
|
||||
array_element_t {"highgui/video/big_buck_bunny.h265",
|
||||
false, false, false}
|
||||
};
|
||||
|
||||
class OneVPL_Source_MFPAsyncDispatcherTest : public ::testing::TestWithParam<array_element_t> {};
|
||||
TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, open_and_decode_file)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
source_t path = findDataFile(std::get<0>(GetParam()));
|
||||
dd_valid_t dd_result = std::get<1>(GetParam());
|
||||
dec_valid_t dec_result = std::get<3>(GetParam());
|
||||
|
||||
// open demux source & check format support
|
||||
std::unique_ptr<MFPAsyncDemuxDataProvider> provider_ptr;
|
||||
try {
|
||||
provider_ptr.reset(new MFPAsyncDemuxDataProvider(path));
|
||||
} catch (...) {
|
||||
EXPECT_FALSE(dd_result);
|
||||
GTEST_SUCCEED();
|
||||
return;
|
||||
}
|
||||
EXPECT_TRUE(dd_result);
|
||||
|
||||
// initialize MFX
|
||||
mfxLoader mfx = MFXLoad();
|
||||
|
||||
mfxConfig cfg_inst_0 = MFXCreateConfig(mfx);
|
||||
EXPECT_TRUE(cfg_inst_0);
|
||||
mfxVariant mfx_param_0;
|
||||
mfx_param_0.Type = MFX_VARIANT_TYPE_U32;
|
||||
mfx_param_0.Data.U32 = provider_ptr->get_mfx_codec_id();
|
||||
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::decoder_id_name(),
|
||||
mfx_param_0), MFX_ERR_NONE);
|
||||
|
||||
// create MFX session
|
||||
mfxSession mfx_session{};
|
||||
mfxStatus sts = MFXCreateSession(mfx, 0, &mfx_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
// create proper bitstream
|
||||
std::shared_ptr<IDataProvider::mfx_bitstream> bitstream{};
|
||||
|
||||
// prepare dec params
|
||||
mfxVideoParam mfxDecParams {};
|
||||
mfxDecParams.mfx.CodecId = mfx_param_0.Data.U32;
|
||||
mfxDecParams.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
|
||||
do {
|
||||
bool fetched = provider_ptr->fetch_bitstream_data(bitstream);
|
||||
if (dec_result) {
|
||||
EXPECT_TRUE(fetched);
|
||||
}
|
||||
sts = MFXVideoDECODE_DecodeHeader(mfx_session, bitstream.get(), &mfxDecParams);
|
||||
EXPECT_TRUE(MFX_ERR_NONE == sts || MFX_ERR_MORE_DATA == sts);
|
||||
} while (sts == MFX_ERR_MORE_DATA && !provider_ptr->empty());
|
||||
|
||||
if (dec_result) {
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
} else {
|
||||
EXPECT_FALSE(MFX_ERR_NONE == sts);
|
||||
}
|
||||
|
||||
MFXVideoDECODE_Close(mfx_session);
|
||||
MFXClose(mfx_session);
|
||||
MFXUnload(mfx);
|
||||
}
|
||||
|
||||
|
||||
TEST_P(OneVPL_Source_MFPAsyncDispatcherTest, choose_dmux_provider)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
|
||||
source_t path = findDataFile(std::get<0>(GetParam()));
|
||||
dd_valid_t dd_result = std::get<1>(GetParam());
|
||||
|
||||
std::shared_ptr<IDataProvider> provider_ptr;
|
||||
|
||||
// choose demux provider for empty CfgParams
|
||||
try {
|
||||
provider_ptr = DataProviderDispatcher::create(path);
|
||||
} catch (...) {
|
||||
EXPECT_FALSE(dd_result);
|
||||
provider_ptr = DataProviderDispatcher::create(path,
|
||||
{ CfgParam::create<std::string>(
|
||||
CfgParam::decoder_id_name(),
|
||||
"MFX_CODEC_HEVC") /* Doesn't matter what codec for RAW here*/});
|
||||
EXPECT_TRUE(std::dynamic_pointer_cast<FileDataProvider>(provider_ptr));
|
||||
GTEST_SUCCEED();
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(dd_result);
|
||||
EXPECT_TRUE(std::dynamic_pointer_cast<MFPAsyncDemuxDataProvider>(provider_ptr));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MFP_VPL_DecodeHeaderTests, OneVPL_Source_MFPAsyncDispatcherTest,
|
||||
testing::ValuesIn(files));
|
||||
|
||||
namespace test {
|
||||
struct IntrusiveAsyncDemuxDataProvider :
|
||||
public cv::gapi::wip::onevpl::MFPAsyncDemuxDataProvider {
|
||||
|
||||
using base_t = cv::gapi::wip::onevpl::MFPAsyncDemuxDataProvider;
|
||||
using base_t::base_t;
|
||||
|
||||
~IntrusiveAsyncDemuxDataProvider() {
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
STDMETHODIMP OnReadSample(HRESULT status, DWORD stream_index,
|
||||
DWORD stream_flag, LONGLONG timestamp,
|
||||
IMFSample *sample_ptr) override {
|
||||
if (IntrusiveAsyncDemuxDataProvider::need_request_next) {
|
||||
return base_t::OnReadSample(status, stream_index, stream_flag,
|
||||
timestamp, sample_ptr);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// implementation methods
|
||||
size_t produce_worker_data(void *key,
|
||||
cv::gapi::wip::onevpl::ComPtrGuard<IMFMediaBuffer> &&buffer,
|
||||
std::shared_ptr<mfx_bitstream> &&staging_stream) override {
|
||||
return base_t::produce_worker_data(key, std::move(buffer),
|
||||
std::move(staging_stream));
|
||||
}
|
||||
|
||||
static bool need_request_next;
|
||||
static bool destroyed;
|
||||
};
|
||||
|
||||
bool IntrusiveAsyncDemuxDataProvider::need_request_next{};
|
||||
bool IntrusiveAsyncDemuxDataProvider::destroyed{};
|
||||
} // namespace test
|
||||
|
||||
TEST(OneVPL_Source_MFPAsyncDemux, sync_flush) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4");
|
||||
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false;
|
||||
const size_t preprocessed_samples_count = 3;
|
||||
{
|
||||
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count);
|
||||
size_t produce_buffer_count = 199 * preprocessed_samples_count;
|
||||
std::thread producer([&provider, produce_buffer_count]() {
|
||||
size_t total_produced_count = 0;
|
||||
for (size_t i = 0; i < produce_buffer_count; i ++) {
|
||||
total_produced_count += provider.produce_worker_data(
|
||||
reinterpret_cast<void*>(i),
|
||||
createCOMPtrGuard<IMFMediaBuffer>(nullptr),
|
||||
{});
|
||||
}
|
||||
});
|
||||
producer.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(test::IntrusiveAsyncDemuxDataProvider::destroyed, true);
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_MFPAsyncDemux, async_flush) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4");
|
||||
test::IntrusiveAsyncDemuxDataProvider::need_request_next = true;
|
||||
const size_t preprocessed_samples_count = 999;
|
||||
{
|
||||
std::shared_ptr<IDataProvider::mfx_bitstream> stream;
|
||||
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count);
|
||||
EXPECT_TRUE(provider.fetch_bitstream_data(stream));
|
||||
EXPECT_TRUE(stream);
|
||||
}
|
||||
|
||||
EXPECT_EQ(test::IntrusiveAsyncDemuxDataProvider::destroyed, true);
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_MFPAsyncDemux, eof_async_detection) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4");
|
||||
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false;
|
||||
const size_t preprocessed_samples_count = 0; // do not ask sample at start
|
||||
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count);
|
||||
std::promise<void> start_consume_data;
|
||||
std::future<void> wait_consume_data = start_consume_data.get_future();
|
||||
|
||||
std::thread fetcher([&provider, &start_consume_data]() {
|
||||
std::shared_ptr<IDataProvider::mfx_bitstream> stream;
|
||||
start_consume_data.set_value();
|
||||
EXPECT_FALSE(provider.fetch_bitstream_data(stream));
|
||||
EXPECT_FALSE(stream);
|
||||
});
|
||||
|
||||
wait_consume_data.wait();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2)); // hope fetched has slept on condition
|
||||
|
||||
test::IntrusiveAsyncDemuxDataProvider::need_request_next = true;
|
||||
provider.OnReadSample(S_OK, 0, MF_SOURCE_READERF_ENDOFSTREAM, 0, nullptr);
|
||||
fetcher.join();
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_MFPAsyncDemux, produce_consume) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
|
||||
source_t path = findDataFile("highgui/video/sample_322x242_15frames.yuv420p.libx265.mp4");
|
||||
test::IntrusiveAsyncDemuxDataProvider::need_request_next = false;
|
||||
const size_t preprocessed_samples_count = 3;
|
||||
test::IntrusiveAsyncDemuxDataProvider provider(path, preprocessed_samples_count);
|
||||
|
||||
std::promise<void> start_consume_data;
|
||||
std::future<void> wait_consume_data = start_consume_data.get_future();
|
||||
size_t produce_buffer_count = 199 * preprocessed_samples_count;
|
||||
std::thread producer([&provider, &wait_consume_data, produce_buffer_count]() {
|
||||
wait_consume_data.wait();
|
||||
size_t total_produced_count = 0;
|
||||
for (size_t i = 0; i < produce_buffer_count; i ++) {
|
||||
std::shared_ptr<IDataProvider::mfx_bitstream> dummy_stream =
|
||||
std::make_shared<IDataProvider::mfx_bitstream>();
|
||||
dummy_stream->DataLength = static_cast<mfxU32>(i); // control block
|
||||
dummy_stream->DataOffset = static_cast<mfxU32>(i); // control block
|
||||
dummy_stream->Data = reinterpret_cast<mfxU8*>(i);
|
||||
total_produced_count = provider.produce_worker_data(
|
||||
dummy_stream->Data,
|
||||
createCOMPtrGuard<IMFMediaBuffer>(nullptr),
|
||||
std::move(dummy_stream));
|
||||
EXPECT_TRUE(total_produced_count <= produce_buffer_count);
|
||||
}
|
||||
});
|
||||
|
||||
std::thread consumer([&provider, &start_consume_data, produce_buffer_count]() {
|
||||
|
||||
start_consume_data.set_value();
|
||||
size_t total_consumed_count = 0;
|
||||
std::shared_ptr<IDataProvider::mfx_bitstream> dummy_stream;
|
||||
size_t stream_idx = 0;
|
||||
do {
|
||||
EXPECT_TRUE(provider.fetch_bitstream_data(dummy_stream));
|
||||
EXPECT_TRUE(dummy_stream);
|
||||
EXPECT_EQ(dummy_stream->DataLength, stream_idx);
|
||||
stream_idx ++;
|
||||
total_consumed_count++;
|
||||
} while (total_consumed_count != produce_buffer_count);
|
||||
});
|
||||
|
||||
producer.join();
|
||||
consumer.join();
|
||||
}
|
||||
}
|
||||
} // namespace opencv_test
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // HAVE_ONEVPL
|
||||
@@ -0,0 +1,359 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2021 Intel Corporation
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include "../common/gapi_tests_common.hpp"
|
||||
|
||||
#include <opencv2/gapi/cpu/core.hpp>
|
||||
#include <opencv2/gapi/ocl/core.hpp>
|
||||
|
||||
#include <opencv2/gapi/streaming/onevpl/source.hpp>
|
||||
|
||||
#ifdef HAVE_DIRECTX
|
||||
#ifdef HAVE_D3D11
|
||||
#pragma comment(lib,"d3d11.lib")
|
||||
|
||||
// get rid of generate macro max/min/etc from DX side
|
||||
#define D3D11_NO_HELPERS
|
||||
#define NOMINMAX
|
||||
#include <d3d11.h>
|
||||
#include <codecvt>
|
||||
#include "opencv2/core/directx.hpp"
|
||||
#undef D3D11_NO_HELPERS
|
||||
#undef NOMINMAX
|
||||
#endif // HAVE_D3D11
|
||||
#endif // HAVE_DIRECTX
|
||||
|
||||
#ifdef __linux__
|
||||
#if defined(HAVE_VA) || defined(HAVE_VA_INTEL)
|
||||
#include "va/va.h"
|
||||
#include "va/va_drm.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL)
|
||||
#endif // __linux__
|
||||
|
||||
#ifdef HAVE_ONEVPL
|
||||
#include "streaming/onevpl/onevpl_export.hpp"
|
||||
#include "streaming/onevpl/cfg_param_device_selector.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
void test_dev_eq(const typename cv::gapi::wip::onevpl::IDeviceSelector::DeviceScoreTable::value_type &scored_device,
|
||||
cv::gapi::wip::onevpl::IDeviceSelector::Score expected_score,
|
||||
cv::gapi::wip::onevpl::AccelType expected_type,
|
||||
cv::gapi::wip::onevpl::Device::Ptr expected_ptr) {
|
||||
EXPECT_EQ(std::get<0>(scored_device), expected_score);
|
||||
EXPECT_EQ(std::get<1>(scored_device).get_type(), expected_type);
|
||||
EXPECT_EQ(std::get<1>(scored_device).get_ptr(), expected_ptr);
|
||||
}
|
||||
|
||||
void test_ctx_eq(const typename cv::gapi::wip::onevpl::IDeviceSelector::DeviceContexts::value_type &ctx,
|
||||
cv::gapi::wip::onevpl::AccelType expected_type,
|
||||
cv::gapi::wip::onevpl::Context::Ptr expected_ptr) {
|
||||
EXPECT_EQ(ctx.get_type(), expected_type);
|
||||
EXPECT_EQ(ctx.get_ptr(), expected_ptr);
|
||||
}
|
||||
|
||||
void test_host_dev_eq(const typename cv::gapi::wip::onevpl::IDeviceSelector::DeviceScoreTable::value_type &scored_device,
|
||||
cv::gapi::wip::onevpl::IDeviceSelector::Score expected_score) {
|
||||
test_dev_eq(scored_device, expected_score,
|
||||
cv::gapi::wip::onevpl::AccelType::HOST, nullptr);
|
||||
}
|
||||
|
||||
void test_host_ctx_eq(const typename cv::gapi::wip::onevpl::IDeviceSelector::DeviceContexts::value_type &ctx) {
|
||||
test_ctx_eq(ctx, cv::gapi::wip::onevpl::AccelType::HOST, nullptr);
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDevice)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
CfgParamDeviceSelector selector;
|
||||
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_host_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector.select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
test_host_ctx_eq(*ctxs.begin());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithEmptyCfgParam)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> empty_params;
|
||||
CfgParamDeviceSelector selector(empty_params);
|
||||
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_host_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority);
|
||||
IDeviceSelector::DeviceContexts ctxs = selector.select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
test_host_ctx_eq(*ctxs.begin());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithAccelNACfgParam)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_no_accel;
|
||||
cfg_params_w_no_accel.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_NA));
|
||||
CfgParamDeviceSelector selector(cfg_params_w_no_accel);
|
||||
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_host_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector.select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
test_host_ctx_eq(*ctxs.begin());
|
||||
}
|
||||
|
||||
#ifdef HAVE_DIRECTX
|
||||
#ifdef HAVE_D3D11
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithEmptyCfgParam_DX11_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> empty_params;
|
||||
CfgParamDeviceSelector selector(empty_params);
|
||||
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_host_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector.select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
test_host_ctx_eq(*ctxs.begin());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithDX11AccelCfgParam_DX11_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_dx11;
|
||||
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
|
||||
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
|
||||
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(cfg_params_w_dx11)));
|
||||
IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices();
|
||||
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority,
|
||||
AccelType::DX11,
|
||||
std::get<1>(*devs.begin()).get_ptr() /* compare just type */);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector_ptr->select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
EXPECT_TRUE(ctxs.begin()->get_ptr());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, NULLDeviceWithDX11AccelCfgParam_DX11_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_dx11;
|
||||
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
|
||||
Device::Ptr empty_device_ptr = nullptr;
|
||||
Context::Ptr empty_ctx_ptr = nullptr;
|
||||
EXPECT_THROW(CfgParamDeviceSelector sel(empty_device_ptr, "GPU",
|
||||
empty_ctx_ptr,
|
||||
cfg_params_w_dx11),
|
||||
std::logic_error); // empty_device_ptr must be invalid
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, ExternalDeviceWithDX11AccelCfgParam_DX11_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
ID3D11Device *device = nullptr;
|
||||
ID3D11DeviceContext* device_context = nullptr;
|
||||
{
|
||||
UINT flags = 0;
|
||||
D3D_FEATURE_LEVEL features[] = { D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
};
|
||||
D3D_FEATURE_LEVEL feature_level;
|
||||
|
||||
// Create the Direct3D 11 API device object and a corresponding context.
|
||||
HRESULT err = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE,
|
||||
nullptr, flags,
|
||||
features,
|
||||
ARRAYSIZE(features), D3D11_SDK_VERSION,
|
||||
&device, &feature_level, &device_context);
|
||||
EXPECT_FALSE(FAILED(err));
|
||||
}
|
||||
|
||||
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
|
||||
std::vector<CfgParam> cfg_params_w_dx11;
|
||||
cfg_params_w_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
|
||||
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(device, "GPU",
|
||||
device_context,
|
||||
cfg_params_w_dx11)));
|
||||
IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices();
|
||||
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority,
|
||||
AccelType::DX11, device);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector_ptr->select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
EXPECT_EQ(reinterpret_cast<ID3D11DeviceContext*>(ctxs.begin()->get_ptr()),
|
||||
device_context);
|
||||
}
|
||||
|
||||
#endif // HAVE_D3D11
|
||||
#endif // HAVE_DIRECTX
|
||||
|
||||
#ifndef HAVE_DIRECTX
|
||||
#ifndef HAVE_D3D11
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DX11DeviceFromCfgParamWithDX11Disabled)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_non_existed_dx11;
|
||||
cfg_params_w_non_existed_dx11.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_D3D11));
|
||||
EXPECT_THROW(CfgParamDeviceSelector{cfg_params_w_non_existed_dx11},
|
||||
std::logic_error);
|
||||
}
|
||||
#endif // HAVE_D3D11
|
||||
#endif // HAVE_DIRECTX
|
||||
|
||||
#ifdef __linux__
|
||||
#if defined(HAVE_VA) || defined(HAVE_VA_INTEL)
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithEmptyCfgParam_VAAPI_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> empty_params;
|
||||
CfgParamDeviceSelector selector(empty_params);
|
||||
IDeviceSelector::DeviceScoreTable devs = selector.select_devices();
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_host_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector.select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
test_host_ctx_eq(*ctxs.begin());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, DefaultDeviceWithVAAPIAccelCfgParam_VAAPI_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_vaapi;
|
||||
cfg_params_w_vaapi.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_VAAPI));
|
||||
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
|
||||
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(cfg_params_w_vaapi)));
|
||||
IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices();
|
||||
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority,
|
||||
AccelType::VAAPI,
|
||||
std::get<1>(*devs.begin()).get_ptr() /* compare just type */);
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector_ptr->select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
EXPECT_FALSE(ctxs.begin()->get_ptr());
|
||||
}
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, NULLDeviceWithVAAPIAccelCfgParam_VAAPI_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> cfg_params_w_vaapi;
|
||||
cfg_params_w_vaapi.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_VAAPI));
|
||||
Device::Ptr empty_device_ptr = nullptr;
|
||||
Context::Ptr empty_ctx_ptr = nullptr;
|
||||
EXPECT_THROW(CfgParamDeviceSelector sel(empty_device_ptr, "GPU",
|
||||
empty_ctx_ptr,
|
||||
cfg_params_w_vaapi),
|
||||
std::logic_error); // empty_device_ptr must be invalid
|
||||
}
|
||||
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, ExternalDeviceWithVAAPIAccelCfgParam_VAAPI_ENABLED)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
VADisplay va_handle = nullptr;
|
||||
struct FileDescriptorRAII {
|
||||
FileDescriptorRAII() :fd (-1) {}
|
||||
~FileDescriptorRAII() { reset(-1); }
|
||||
void reset(int d) {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
fd = d;
|
||||
}
|
||||
operator int() { return fd; }
|
||||
private:
|
||||
FileDescriptorRAII(FileDescriptorRAII& src) = delete;
|
||||
FileDescriptorRAII& operator=(FileDescriptorRAII& src) = delete;
|
||||
FileDescriptorRAII(FileDescriptorRAII&& src) = delete;
|
||||
FileDescriptorRAII& operator=(FileDescriptorRAII&& src) = delete;
|
||||
int fd = -1;
|
||||
};
|
||||
static const char *predefined_vaapi_devices_list[] {"/dev/dri/renderD128",
|
||||
"/dev/dri/renderD129",
|
||||
"/dev/dri/card0",
|
||||
"/dev/dri/card1",
|
||||
nullptr};
|
||||
|
||||
FileDescriptorRAII device_fd;
|
||||
for (const char **device_path = predefined_vaapi_devices_list;
|
||||
*device_path != nullptr; device_path++) {
|
||||
device_fd.reset(open(*device_path, O_RDWR));
|
||||
if (device_fd < 0) {
|
||||
continue;
|
||||
}
|
||||
va_handle = vaGetDisplayDRM(device_fd);
|
||||
if (!va_handle) {
|
||||
continue;
|
||||
}
|
||||
int major_version = 0, minor_version = 0;
|
||||
VAStatus status {};
|
||||
status = vaInitialize(va_handle, &major_version, &minor_version);
|
||||
if (VA_STATUS_SUCCESS != status) {
|
||||
close(device_fd);
|
||||
va_handle = nullptr;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
EXPECT_TRUE(device_fd != -1);
|
||||
EXPECT_TRUE(va_handle);
|
||||
auto device = cv::util::make_optional(
|
||||
cv::gapi::wip::onevpl::create_vaapi_device(reinterpret_cast<void*>(va_handle),
|
||||
"GPU", device_fd));
|
||||
auto device_context = cv::util::make_optional(
|
||||
cv::gapi::wip::onevpl::create_vaapi_context(nullptr));
|
||||
|
||||
std::unique_ptr<CfgParamDeviceSelector> selector_ptr;
|
||||
std::vector<CfgParam> cfg_params_w_vaapi;
|
||||
cfg_params_w_vaapi.push_back(CfgParam::create_acceleration_mode(MFX_ACCEL_MODE_VIA_VAAPI));
|
||||
EXPECT_NO_THROW(selector_ptr.reset(new CfgParamDeviceSelector(device.value(),
|
||||
device_context.value(),
|
||||
cfg_params_w_vaapi)));
|
||||
IDeviceSelector::DeviceScoreTable devs = selector_ptr->select_devices();
|
||||
|
||||
EXPECT_TRUE(devs.size() == 1);
|
||||
test_dev_eq(*devs.begin(), IDeviceSelector::Score::MaxActivePriority,
|
||||
AccelType::VAAPI, device.value().get_ptr());
|
||||
|
||||
IDeviceSelector::DeviceContexts ctxs = selector_ptr->select_context();
|
||||
EXPECT_TRUE(ctxs.size() == 1);
|
||||
EXPECT_EQ(reinterpret_cast<void*>(ctxs.begin()->get_ptr()),
|
||||
device_context.value().get_ptr());
|
||||
}
|
||||
#endif // defined(HAVE_VA) || defined(HAVE_VA_INTEL)
|
||||
#endif // #ifdef __linux__
|
||||
|
||||
TEST(OneVPL_Source_Device_Selector_CfgParam, UnknownPtrDeviceFromCfgParam)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::vector<CfgParam> empty_params;
|
||||
Device::Ptr empty_device_ptr = nullptr;
|
||||
Context::Ptr empty_ctx_ptr = nullptr;
|
||||
EXPECT_THROW(CfgParamDeviceSelector sel(empty_device_ptr, "",
|
||||
empty_ctx_ptr,
|
||||
empty_params),
|
||||
std::logic_error); // params must describe device_ptr explicitly
|
||||
}
|
||||
}
|
||||
} // namespace opencv_test
|
||||
#endif // HAVE_ONEVPL
|
||||
@@ -0,0 +1,736 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (C) 2022 Intel Corporation
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
|
||||
#include "../common/gapi_tests_common.hpp"
|
||||
#include "../common/gapi_streaming_tests_common.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <tuple>
|
||||
|
||||
#include <opencv2/gapi/media.hpp>
|
||||
#include <opencv2/gapi/cpu/core.hpp>
|
||||
#include <opencv2/gapi/cpu/imgproc.hpp>
|
||||
|
||||
#include <opencv2/gapi/fluid/core.hpp>
|
||||
#include <opencv2/gapi/fluid/imgproc.hpp>
|
||||
#include <opencv2/gapi/fluid/gfluidkernel.hpp>
|
||||
|
||||
#include <opencv2/gapi/ocl/core.hpp>
|
||||
#include <opencv2/gapi/ocl/imgproc.hpp>
|
||||
|
||||
#include <opencv2/gapi/streaming/cap.hpp>
|
||||
#include <opencv2/gapi/streaming/desync.hpp>
|
||||
#include <opencv2/gapi/streaming/format.hpp>
|
||||
|
||||
#ifdef HAVE_ONEVPL
|
||||
|
||||
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
|
||||
#include "streaming/onevpl/file_data_provider.hpp"
|
||||
#include "streaming/onevpl/cfg_param_device_selector.hpp"
|
||||
|
||||
#include "streaming/onevpl/accelerators/surface/surface.hpp"
|
||||
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
|
||||
#include "streaming/onevpl/accelerators/surface/dx11_frame_adapter.hpp"
|
||||
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp"
|
||||
#include "streaming/onevpl/accelerators/accel_policy_dx11.hpp"
|
||||
#include "streaming/onevpl/accelerators/accel_policy_va_api.hpp"
|
||||
#include "streaming/onevpl/accelerators/dx11_alloc_resource.hpp"
|
||||
#include "streaming/onevpl/accelerators/utils/shared_lock.hpp"
|
||||
#define private public
|
||||
#define protected public
|
||||
#include "streaming/onevpl/engine/decode/decode_engine_legacy.hpp"
|
||||
#include "streaming/onevpl/engine/decode/decode_session.hpp"
|
||||
|
||||
#include "streaming/onevpl/engine/preproc/preproc_engine.hpp"
|
||||
#include "streaming/onevpl/engine/preproc/preproc_session.hpp"
|
||||
#include "streaming/onevpl/engine/preproc/preproc_dispatcher.hpp"
|
||||
|
||||
#include "streaming/onevpl/engine/transcode/transcode_engine_legacy.hpp"
|
||||
#include "streaming/onevpl/engine/transcode/transcode_session.hpp"
|
||||
#undef protected
|
||||
#undef private
|
||||
#include "logger.hpp"
|
||||
|
||||
#define ALIGN16(value) (((value + 15) >> 4) << 4)
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template<class ProcessingEngine>
|
||||
cv::MediaFrame extract_decoded_frame(mfxSession sessId, ProcessingEngine& engine) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
ProcessingEngineBase::ExecutionStatus status = ProcessingEngineBase::ExecutionStatus::Continue;
|
||||
while (0 == engine.get_ready_frames_count() &&
|
||||
status == ProcessingEngineBase::ExecutionStatus::Continue) {
|
||||
status = engine.process(sessId);
|
||||
}
|
||||
|
||||
if (engine.get_ready_frames_count() == 0) {
|
||||
GAPI_LOG_WARNING(nullptr, "failed: cannot obtain preprocessed frames, last status: " <<
|
||||
ProcessingEngineBase::status_to_string(status));
|
||||
throw std::runtime_error("cannot finalize VPP preprocessing operation");
|
||||
}
|
||||
cv::gapi::wip::Data data;
|
||||
engine.get_frame(data);
|
||||
return cv::util::get<cv::MediaFrame>(data);
|
||||
}
|
||||
|
||||
std::tuple<mfxLoader, mfxConfig> prepare_mfx(int mfx_codec, int mfx_accel_mode) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
mfxLoader mfx = MFXLoad();
|
||||
mfxConfig cfg_inst_0 = MFXCreateConfig(mfx);
|
||||
EXPECT_TRUE(cfg_inst_0);
|
||||
mfxVariant mfx_param_0;
|
||||
mfx_param_0.Type = MFX_VARIANT_TYPE_U32;
|
||||
mfx_param_0.Data.U32 = MFX_IMPL_TYPE_HARDWARE;
|
||||
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_0,(mfxU8 *)CfgParam::implementation_name(),
|
||||
mfx_param_0), MFX_ERR_NONE);
|
||||
|
||||
mfxConfig cfg_inst_1 = MFXCreateConfig(mfx);
|
||||
EXPECT_TRUE(cfg_inst_1);
|
||||
mfxVariant mfx_param_1;
|
||||
mfx_param_1.Type = MFX_VARIANT_TYPE_U32;
|
||||
mfx_param_1.Data.U32 = mfx_accel_mode;
|
||||
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_1,(mfxU8 *)CfgParam::acceleration_mode_name(),
|
||||
mfx_param_1), MFX_ERR_NONE);
|
||||
|
||||
mfxConfig cfg_inst_2 = MFXCreateConfig(mfx);
|
||||
EXPECT_TRUE(cfg_inst_2);
|
||||
mfxVariant mfx_param_2;
|
||||
mfx_param_2.Type = MFX_VARIANT_TYPE_U32;
|
||||
mfx_param_2.Data.U32 = mfx_codec;
|
||||
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_2,(mfxU8 *)CfgParam::decoder_id_name(),
|
||||
mfx_param_2), MFX_ERR_NONE);
|
||||
|
||||
mfxConfig cfg_inst_3 = MFXCreateConfig(mfx);
|
||||
EXPECT_TRUE(cfg_inst_3);
|
||||
mfxVariant mfx_param_3;
|
||||
mfx_param_3.Type = MFX_VARIANT_TYPE_U32;
|
||||
mfx_param_3.Data.U32 = MFX_EXTBUFF_VPP_SCALING;
|
||||
EXPECT_EQ(MFXSetConfigFilterProperty(cfg_inst_3,
|
||||
(mfxU8 *)"mfxImplDescription.mfxVPPDescription.filter.FilterFourCC",
|
||||
mfx_param_3), MFX_ERR_NONE);
|
||||
return std::make_tuple(mfx, cfg_inst_3);
|
||||
}
|
||||
|
||||
static std::unique_ptr<cv::gapi::wip::onevpl::VPLAccelerationPolicy>
|
||||
create_accel_policy_from_int(int accel,
|
||||
std::shared_ptr<cv::gapi::wip::onevpl::IDeviceSelector> selector) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
std::unique_ptr<VPLAccelerationPolicy> decode_accel_policy;
|
||||
if (accel == MFX_ACCEL_MODE_VIA_D3D11) {
|
||||
decode_accel_policy.reset (new VPLDX11AccelerationPolicy(selector));
|
||||
} else if (accel == MFX_ACCEL_MODE_VIA_VAAPI) {
|
||||
decode_accel_policy.reset (new VPLVAAPIAccelerationPolicy(selector));
|
||||
}
|
||||
EXPECT_TRUE(decode_accel_policy.get());
|
||||
return decode_accel_policy;
|
||||
}
|
||||
|
||||
static std::unique_ptr<cv::gapi::wip::onevpl::VPLAccelerationPolicy>
|
||||
create_accel_policy_from_int(int &accel,
|
||||
std::vector<cv::gapi::wip::onevpl::CfgParam> &out_cfg_params) {
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
out_cfg_params.push_back(CfgParam::create_acceleration_mode(accel));
|
||||
return create_accel_policy_from_int(accel, std::make_shared<CfgParamDeviceSelector>(out_cfg_params));
|
||||
}
|
||||
|
||||
class SafeQueue {
|
||||
public:
|
||||
void push(cv::MediaFrame&& f) {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
queue.push(std::move(f));
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
cv::MediaFrame pop() {
|
||||
cv::MediaFrame ret;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [this] () {
|
||||
return !queue.empty();
|
||||
});
|
||||
ret = queue.front();
|
||||
queue.pop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void push_stop() {
|
||||
push(cv::MediaFrame::Create<IStopAdapter>());
|
||||
}
|
||||
|
||||
static bool is_stop(const cv::MediaFrame &f) {
|
||||
try {
|
||||
return f.get<IStopAdapter>();
|
||||
} catch(...) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
struct IStopAdapter final : public cv::MediaFrame::IAdapter {
|
||||
~IStopAdapter() {}
|
||||
cv::GFrameDesc meta() const { return {}; };
|
||||
MediaFrame::View access(MediaFrame::Access) { return {{}, {}}; };
|
||||
};
|
||||
private:
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
std::queue<cv::MediaFrame> queue;
|
||||
};
|
||||
|
||||
struct EmptyDataProvider : public cv::gapi::wip::onevpl::IDataProvider {
|
||||
|
||||
bool empty() const override {
|
||||
return true;
|
||||
}
|
||||
mfx_codec_id_type get_mfx_codec_id() const override {
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
}
|
||||
bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &) override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
using source_t = std::string;
|
||||
using decoder_t = int;
|
||||
using acceleration_t = int;
|
||||
using out_frame_info_t = cv::GFrameDesc;
|
||||
using preproc_args_t = std::tuple<source_t, decoder_t, acceleration_t, out_frame_info_t>;
|
||||
|
||||
static cv::util::optional<cv::Rect> empty_roi;
|
||||
|
||||
class VPPPreprocParams : public ::testing::TestWithParam<preproc_args_t> {};
|
||||
|
||||
#if defined(HAVE_DIRECTX) && defined(HAVE_D3D11)
|
||||
#define UT_ACCEL_TYPE MFX_ACCEL_MODE_VIA_D3D11
|
||||
#elif __linux__
|
||||
#define UT_ACCEL_TYPE MFX_ACCEL_MODE_VIA_VAAPI
|
||||
#else
|
||||
#define UT_ACCEL_TYPE -1
|
||||
#endif
|
||||
|
||||
preproc_args_t files[] = {
|
||||
preproc_args_t {"highgui/video/big_buck_bunny.h264",
|
||||
MFX_CODEC_AVC, UT_ACCEL_TYPE,
|
||||
cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1080}}},
|
||||
preproc_args_t {"highgui/video/big_buck_bunny.h265",
|
||||
MFX_CODEC_HEVC, UT_ACCEL_TYPE,
|
||||
cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1280}}}
|
||||
};
|
||||
|
||||
class OneVPL_PreproEngineTest : public ::testing::TestWithParam<acceleration_t> {};
|
||||
TEST_P(OneVPL_PreproEngineTest, functional_single_thread)
|
||||
{
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
using namespace cv::gapi::wip;
|
||||
|
||||
int accel_type = GetParam();
|
||||
std::vector<CfgParam> cfg_params_w_accel;
|
||||
std::unique_ptr<VPLAccelerationPolicy> decode_accel_policy = create_accel_policy_from_int(accel_type, cfg_params_w_accel);
|
||||
|
||||
// create file data provider
|
||||
std::string file_path = findDataFile("highgui/video/big_buck_bunny.h265");
|
||||
std::shared_ptr<IDataProvider> data_provider(new FileDataProvider(file_path,
|
||||
{CfgParam::create_decoder_id(MFX_CODEC_HEVC)}));
|
||||
|
||||
mfxLoader mfx{};
|
||||
mfxConfig mfx_cfg{};
|
||||
std::tie(mfx, mfx_cfg) = prepare_mfx(MFX_CODEC_HEVC, accel_type);
|
||||
|
||||
// create decode session
|
||||
mfxSession mfx_decode_session{};
|
||||
mfxStatus sts = MFXCreateSession(mfx, 0, &mfx_decode_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
// create decode engine
|
||||
auto device_selector = decode_accel_policy->get_device_selector();
|
||||
VPLLegacyDecodeEngine decode_engine(std::move(decode_accel_policy));
|
||||
auto sess_ptr = decode_engine.initialize_session(mfx_decode_session,
|
||||
cfg_params_w_accel,
|
||||
data_provider);
|
||||
|
||||
// simulate net info
|
||||
cv::GFrameDesc required_frame_param {cv::MediaFormat::NV12,
|
||||
{1920, 1080}};
|
||||
|
||||
// create VPP preproc engine
|
||||
VPPPreprocEngine preproc_engine(create_accel_policy_from_int(accel_type, device_selector));
|
||||
|
||||
// launch pipeline
|
||||
// 1) decode frame
|
||||
cv::MediaFrame first_decoded_frame;
|
||||
ASSERT_NO_THROW(first_decoded_frame = extract_decoded_frame(sess_ptr->session, decode_engine));
|
||||
cv::GFrameDesc first_frame_decoded_desc = first_decoded_frame.desc();
|
||||
|
||||
// 1.5) create preproc session based on frame description & network info
|
||||
cv::util::optional<pp_params> first_pp_params = preproc_engine.is_applicable(first_decoded_frame);
|
||||
ASSERT_TRUE(first_pp_params.has_value());
|
||||
pp_session first_pp_sess = preproc_engine.initialize_preproc(first_pp_params.value(),
|
||||
required_frame_param);
|
||||
|
||||
// 2) make preproc using incoming decoded frame & preproc session
|
||||
cv::MediaFrame first_pp_frame = preproc_engine.run_sync(first_pp_sess,
|
||||
first_decoded_frame,
|
||||
empty_roi);
|
||||
cv::GFrameDesc first_outcome_pp_desc = first_pp_frame.desc();
|
||||
ASSERT_FALSE(first_frame_decoded_desc == first_outcome_pp_desc);
|
||||
|
||||
// do not hold media frames because they share limited DX11 surface pool resources
|
||||
first_decoded_frame = cv::MediaFrame();
|
||||
first_pp_frame = cv::MediaFrame();
|
||||
|
||||
// make test in loop
|
||||
bool in_progress = false;
|
||||
int frames_processed_count = 1;
|
||||
const auto &first_pp_param_value_impl =
|
||||
cv::util::get<cv::gapi::wip::onevpl::vpp_pp_params>(first_pp_params.value().value);
|
||||
try {
|
||||
while(true) {
|
||||
cv::MediaFrame decoded_frame = extract_decoded_frame(sess_ptr->session, decode_engine);
|
||||
in_progress = true;
|
||||
ASSERT_EQ(decoded_frame.desc(), first_frame_decoded_desc);
|
||||
|
||||
cv::util::optional<pp_params> params = preproc_engine.is_applicable(decoded_frame);
|
||||
ASSERT_TRUE(params.has_value());
|
||||
const auto &cur_pp_param_value_impl =
|
||||
cv::util::get<cv::gapi::wip::onevpl::vpp_pp_params>(params.value().value);
|
||||
|
||||
ASSERT_EQ(first_pp_param_value_impl.handle, cur_pp_param_value_impl.handle);
|
||||
ASSERT_TRUE(FrameInfoComparator::equal_to(first_pp_param_value_impl.info, cur_pp_param_value_impl.info));
|
||||
|
||||
pp_session pp_sess = preproc_engine.initialize_preproc(params.value(),
|
||||
required_frame_param);
|
||||
ASSERT_EQ(pp_sess.get<vpp_pp_session>().handle.get(),
|
||||
first_pp_sess.get<vpp_pp_session>().handle.get());
|
||||
|
||||
cv::MediaFrame pp_frame = preproc_engine.run_sync(pp_sess,
|
||||
decoded_frame,
|
||||
empty_roi);
|
||||
cv::GFrameDesc pp_desc = pp_frame.desc();
|
||||
ASSERT_TRUE(pp_desc == first_outcome_pp_desc);
|
||||
in_progress = false;
|
||||
frames_processed_count++;
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
// test if interruption has happened
|
||||
ASSERT_FALSE(in_progress);
|
||||
ASSERT_NE(frames_processed_count, 1);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(OneVPL_Source_PreprocEngine, OneVPL_PreproEngineTest,
|
||||
testing::Values(UT_ACCEL_TYPE));
|
||||
|
||||
static void decode_function(cv::gapi::wip::onevpl::VPLLegacyDecodeEngine &decode_engine,
|
||||
cv::gapi::wip::onevpl::ProcessingEngineBase::session_ptr sess_ptr,
|
||||
SafeQueue &queue, int &decoded_number) {
|
||||
// decode first frame
|
||||
{
|
||||
cv::MediaFrame decoded_frame;
|
||||
ASSERT_NO_THROW(decoded_frame = extract_decoded_frame(sess_ptr->session, decode_engine));
|
||||
queue.push(std::move(decoded_frame));
|
||||
}
|
||||
|
||||
// launch pipeline
|
||||
try {
|
||||
while(true) {
|
||||
queue.push(extract_decoded_frame(sess_ptr->session, decode_engine));
|
||||
decoded_number++;
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
// send stop
|
||||
queue.push_stop();
|
||||
}
|
||||
|
||||
static void preproc_function(cv::gapi::wip::IPreprocEngine &preproc_engine, SafeQueue&queue,
|
||||
int &preproc_number, const out_frame_info_t &required_frame_param,
|
||||
const cv::util::optional<cv::Rect> &roi_rect = {}) {
|
||||
using namespace cv::gapi::wip;
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
// create preproc session based on frame description & network info
|
||||
cv::MediaFrame first_decoded_frame = queue.pop();
|
||||
cv::util::optional<pp_params> first_pp_params = preproc_engine.is_applicable(first_decoded_frame);
|
||||
ASSERT_TRUE(first_pp_params.has_value());
|
||||
pp_session first_pp_sess =
|
||||
preproc_engine.initialize_preproc(first_pp_params.value(),
|
||||
required_frame_param);
|
||||
|
||||
// make preproc using incoming decoded frame & preproc session
|
||||
cv::MediaFrame first_pp_frame = preproc_engine.run_sync(first_pp_sess,
|
||||
first_decoded_frame,
|
||||
roi_rect);
|
||||
cv::GFrameDesc first_outcome_pp_desc = first_pp_frame.desc();
|
||||
|
||||
// do not hold media frames because they share limited DX11 surface pool resources
|
||||
first_decoded_frame = cv::MediaFrame();
|
||||
first_pp_frame = cv::MediaFrame();
|
||||
|
||||
// launch pipeline
|
||||
bool in_progress = false;
|
||||
// let's allow counting of preprocessed frames to check this value later:
|
||||
// Currently, it looks redundant to implement any kind of graceful shutdown logic
|
||||
// in this test - so let's apply agreement that media source is processed
|
||||
// successfully when preproc_number != 1 in result.
|
||||
// Specific validation logic which adhere to explicit counter value may be implemented
|
||||
// in particular test scope
|
||||
preproc_number = 1;
|
||||
try {
|
||||
while(true) {
|
||||
cv::MediaFrame decoded_frame = queue.pop();
|
||||
if (SafeQueue::is_stop(decoded_frame)) {
|
||||
break;
|
||||
}
|
||||
in_progress = true;
|
||||
|
||||
cv::util::optional<pp_params> params = preproc_engine.is_applicable(decoded_frame);
|
||||
ASSERT_TRUE(params.has_value());
|
||||
const auto &vpp_params = params.value().get<vpp_pp_params>();
|
||||
const auto &first_vpp_params = first_pp_params.value().get<vpp_pp_params>();
|
||||
ASSERT_EQ(vpp_params.handle, first_vpp_params.handle);
|
||||
ASSERT_TRUE(0 == memcmp(&vpp_params.info, &first_vpp_params.info, sizeof(mfxFrameInfo)));
|
||||
|
||||
pp_session pp_sess = preproc_engine.initialize_preproc(params.value(),
|
||||
required_frame_param);
|
||||
ASSERT_EQ(pp_sess.get<vpp_pp_session>().handle.get(),
|
||||
first_pp_sess.get<vpp_pp_session>().handle.get());
|
||||
|
||||
cv::MediaFrame pp_frame = preproc_engine.run_sync(pp_sess, decoded_frame, empty_roi);
|
||||
cv::GFrameDesc pp_desc = pp_frame.desc();
|
||||
ASSERT_TRUE(pp_desc == first_outcome_pp_desc);
|
||||
in_progress = false;
|
||||
preproc_number++;
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
// test if interruption has happened
|
||||
ASSERT_FALSE(in_progress);
|
||||
ASSERT_NE(preproc_number, 1);
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
static void multi_source_preproc_function(size_t source_num,
|
||||
cv::gapi::wip::IPreprocEngine &preproc_engine, SafeQueue&queue,
|
||||
int &preproc_number, const out_frame_info_t &required_frame_param,
|
||||
const cv::util::optional<cv::Rect> &roi_rect = {}) {
|
||||
using namespace cv::gapi::wip;
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
// create preproc session based on frame description & network info
|
||||
cv::MediaFrame first_decoded_frame = queue.pop();
|
||||
cv::util::optional<pp_params> first_pp_params = preproc_engine.is_applicable(first_decoded_frame);
|
||||
ASSERT_TRUE(first_pp_params.has_value());
|
||||
pp_session first_pp_sess =
|
||||
preproc_engine.initialize_preproc(first_pp_params.value(),
|
||||
required_frame_param);
|
||||
|
||||
// make preproc using incoming decoded frame & preproc session
|
||||
cv::MediaFrame first_pp_frame = preproc_engine.run_sync(first_pp_sess,
|
||||
first_decoded_frame,
|
||||
roi_rect);
|
||||
cv::GFrameDesc first_outcome_pp_desc = first_pp_frame.desc();
|
||||
|
||||
// do not hold media frames because they share limited DX11 surface pool resources
|
||||
first_decoded_frame = cv::MediaFrame();
|
||||
first_pp_frame = cv::MediaFrame();
|
||||
|
||||
// launch pipeline
|
||||
bool in_progress = false;
|
||||
preproc_number = 1;
|
||||
size_t received_stop_count = 0;
|
||||
try {
|
||||
while(received_stop_count != source_num) {
|
||||
cv::MediaFrame decoded_frame = queue.pop();
|
||||
if (SafeQueue::is_stop(decoded_frame)) {
|
||||
++received_stop_count;
|
||||
continue;
|
||||
}
|
||||
in_progress = true;
|
||||
|
||||
cv::util::optional<pp_params> params = preproc_engine.is_applicable(decoded_frame);
|
||||
ASSERT_TRUE(params.has_value());
|
||||
|
||||
pp_session pp_sess = preproc_engine.initialize_preproc(params.value(),
|
||||
required_frame_param);
|
||||
cv::MediaFrame pp_frame = preproc_engine.run_sync(pp_sess, decoded_frame, empty_roi);
|
||||
cv::GFrameDesc pp_desc = pp_frame.desc();
|
||||
ASSERT_TRUE(pp_desc == first_outcome_pp_desc);
|
||||
in_progress = false;
|
||||
decoded_frame = cv::MediaFrame();
|
||||
preproc_number++;
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
GAPI_LOG_WARNING(nullptr, "Caught exception in preproc worker: " << ex.what());
|
||||
}
|
||||
|
||||
// test if interruption has happened
|
||||
if (in_progress) {
|
||||
while (true) {
|
||||
cv::MediaFrame decoded_frame = queue.pop();
|
||||
if (SafeQueue::is_stop(decoded_frame)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_FALSE(in_progress);
|
||||
ASSERT_NE(preproc_number, 1);
|
||||
}
|
||||
#endif // __WIN32__
|
||||
|
||||
using roi_t = cv::util::optional<cv::Rect>;
|
||||
using preproc_roi_args_t = decltype(std::tuple_cat(std::declval<preproc_args_t>(),
|
||||
std::declval<std::tuple<roi_t>>()));
|
||||
class VPPPreprocROIParams : public ::testing::TestWithParam<preproc_roi_args_t> {};
|
||||
TEST_P(VPPPreprocROIParams, functional_roi_different_threads)
|
||||
{
|
||||
using namespace cv::gapi::wip;
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
source_t file_path;
|
||||
decoder_t decoder_id;
|
||||
acceleration_t accel;
|
||||
out_frame_info_t required_frame_param;
|
||||
roi_t opt_roi;
|
||||
std::tie(file_path, decoder_id, accel, required_frame_param, opt_roi) = GetParam();
|
||||
|
||||
file_path = findDataFile(file_path);
|
||||
|
||||
std::vector<CfgParam> cfg_params_w_accel;
|
||||
std::unique_ptr<VPLAccelerationPolicy> decode_accel_policy = create_accel_policy_from_int(accel, cfg_params_w_accel);
|
||||
|
||||
// create file data provider
|
||||
std::shared_ptr<IDataProvider> data_provider(new FileDataProvider(file_path,
|
||||
{CfgParam::create_decoder_id(decoder_id)}));
|
||||
|
||||
mfxLoader mfx{};
|
||||
mfxConfig mfx_cfg{};
|
||||
std::tie(mfx, mfx_cfg) = prepare_mfx(decoder_id, accel);
|
||||
|
||||
// create decode session
|
||||
mfxSession mfx_decode_session{};
|
||||
mfxStatus sts = MFXCreateSession(mfx, 0, &mfx_decode_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
// create decode engine
|
||||
auto device_selector = decode_accel_policy->get_device_selector();
|
||||
VPLLegacyDecodeEngine decode_engine(std::move(decode_accel_policy));
|
||||
auto sess_ptr = decode_engine.initialize_session(mfx_decode_session,
|
||||
cfg_params_w_accel,
|
||||
data_provider);
|
||||
|
||||
// create VPP preproc engine
|
||||
VPPPreprocEngine preproc_engine(create_accel_policy_from_int(accel, device_selector));
|
||||
|
||||
// launch threads
|
||||
SafeQueue queue;
|
||||
int decoded_number = 1;
|
||||
int preproc_number = 0;
|
||||
|
||||
std::thread decode_thread(decode_function, std::ref(decode_engine), sess_ptr,
|
||||
std::ref(queue), std::ref(decoded_number));
|
||||
std::thread preproc_thread(preproc_function, std::ref(preproc_engine),
|
||||
std::ref(queue), std::ref(preproc_number),
|
||||
std::cref(required_frame_param),
|
||||
std::cref(opt_roi));
|
||||
|
||||
decode_thread.join();
|
||||
preproc_thread.join();
|
||||
ASSERT_EQ(preproc_number, decoded_number);
|
||||
}
|
||||
|
||||
preproc_roi_args_t files_w_roi[] = {
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h264",
|
||||
MFX_CODEC_AVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1080}}},
|
||||
roi_t{cv::Rect{0,0,50,50}}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h264",
|
||||
MFX_CODEC_AVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1080}}},
|
||||
roi_t{}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h264",
|
||||
MFX_CODEC_AVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1080}}},
|
||||
roi_t{cv::Rect{0,0,100,100}}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h264",
|
||||
MFX_CODEC_AVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1080}}},
|
||||
roi_t{cv::Rect{100,100,200,200}}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h265",
|
||||
MFX_CODEC_HEVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1280}}},
|
||||
roi_t{cv::Rect{0,0,100,100}}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h265",
|
||||
MFX_CODEC_HEVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1280}}},
|
||||
roi_t{}},
|
||||
preproc_roi_args_t {"highgui/video/big_buck_bunny.h265",
|
||||
MFX_CODEC_HEVC, UT_ACCEL_TYPE,
|
||||
out_frame_info_t{cv::GFrameDesc {cv::MediaFormat::NV12, {1920, 1280}}},
|
||||
roi_t{cv::Rect{100,100,200,200}}}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(OneVPL_Source_PreprocEngineROI, VPPPreprocROIParams,
|
||||
testing::ValuesIn(files_w_roi));
|
||||
|
||||
|
||||
using VPPInnerPreprocParams = VPPPreprocParams;
|
||||
TEST_P(VPPInnerPreprocParams, functional_inner_preproc_size)
|
||||
{
|
||||
using namespace cv::gapi::wip;
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
source_t file_path;
|
||||
decoder_t decoder_id;
|
||||
acceleration_t accel;
|
||||
out_frame_info_t required_frame_param;
|
||||
std::tie(file_path, decoder_id, accel, required_frame_param) = GetParam();
|
||||
|
||||
file_path = findDataFile(file_path);
|
||||
|
||||
std::vector<CfgParam> cfg_params_w_accel_vpp;
|
||||
|
||||
// create accel policy
|
||||
std::unique_ptr<VPLAccelerationPolicy> accel_policy = create_accel_policy_from_int(accel, cfg_params_w_accel_vpp);
|
||||
|
||||
// create file data provider
|
||||
std::shared_ptr<IDataProvider> data_provider(new FileDataProvider(file_path,
|
||||
{CfgParam::create_decoder_id(decoder_id)}));
|
||||
|
||||
// create decode session
|
||||
mfxLoader mfx{};
|
||||
mfxConfig mfx_cfg{};
|
||||
std::tie(mfx, mfx_cfg) = prepare_mfx(decoder_id, accel);
|
||||
|
||||
mfxSession mfx_decode_session{};
|
||||
mfxStatus sts = MFXCreateSession(mfx, 0, &mfx_decode_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
// fill vpp params beforehand: resolution
|
||||
cfg_params_w_accel_vpp.push_back(CfgParam::create_vpp_out_width(
|
||||
static_cast<uint16_t>(required_frame_param.size.width)));
|
||||
cfg_params_w_accel_vpp.push_back(CfgParam::create_vpp_out_height(
|
||||
static_cast<uint16_t>(required_frame_param.size.height)));
|
||||
|
||||
// create transcode engine
|
||||
auto device_selector = accel_policy->get_device_selector();
|
||||
VPLLegacyTranscodeEngine engine(std::move(accel_policy));
|
||||
auto sess_ptr = engine.initialize_session(mfx_decode_session,
|
||||
cfg_params_w_accel_vpp,
|
||||
data_provider);
|
||||
// make test in loop
|
||||
bool in_progress = false;
|
||||
int frames_processed_count = 1;
|
||||
try {
|
||||
while(true) {
|
||||
cv::MediaFrame decoded_frame = extract_decoded_frame(sess_ptr->session, engine);
|
||||
in_progress = true;
|
||||
ASSERT_EQ(decoded_frame.desc().size.width,
|
||||
ALIGN16(required_frame_param.size.width));
|
||||
ASSERT_EQ(decoded_frame.desc().size.height,
|
||||
ALIGN16(required_frame_param.size.height));
|
||||
ASSERT_EQ(decoded_frame.desc().fmt, required_frame_param.fmt);
|
||||
frames_processed_count++;
|
||||
in_progress = false;
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
// test if interruption has happened
|
||||
ASSERT_FALSE(in_progress);
|
||||
ASSERT_NE(frames_processed_count, 1);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(OneVPL_Source_PreprocInner, VPPInnerPreprocParams,
|
||||
testing::ValuesIn(files));
|
||||
|
||||
// enable only for WIN32 because there are not CPU processing on Linux by default
|
||||
#ifdef __WIN32__
|
||||
class VPPPreprocDispatcherROIParams : public ::testing::TestWithParam<preproc_roi_args_t> {};
|
||||
TEST_P(VPPPreprocDispatcherROIParams, functional_roi_different_threads)
|
||||
{
|
||||
using namespace cv::gapi::wip;
|
||||
using namespace cv::gapi::wip::onevpl;
|
||||
source_t file_path;
|
||||
decoder_t decoder_id;
|
||||
acceleration_t accel = 0;
|
||||
out_frame_info_t required_frame_param;
|
||||
roi_t opt_roi;
|
||||
std::tie(file_path, decoder_id, accel, required_frame_param, opt_roi) = GetParam();
|
||||
|
||||
file_path = findDataFile(file_path);
|
||||
|
||||
std::vector<CfgParam> cfg_params_w_accel;
|
||||
std::unique_ptr<VPLAccelerationPolicy> decode_accel_policy = create_accel_policy_from_int(accel, cfg_params_w_accel);
|
||||
|
||||
// create file data provider
|
||||
std::shared_ptr<IDataProvider> data_provider(new FileDataProvider(file_path,
|
||||
{CfgParam::create_decoder_id(decoder_id)}));
|
||||
std::shared_ptr<IDataProvider> cpu_data_provider(new FileDataProvider(file_path,
|
||||
{CfgParam::create_decoder_id(decoder_id)}));
|
||||
|
||||
mfxLoader mfx{};
|
||||
mfxConfig mfx_cfg{};
|
||||
std::tie(mfx, mfx_cfg) = prepare_mfx(decoder_id, accel);
|
||||
|
||||
// create decode session
|
||||
mfxSession mfx_decode_session{};
|
||||
mfxStatus sts = MFXCreateSession(mfx, 0, &mfx_decode_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
mfxSession mfx_cpu_decode_session{};
|
||||
sts = MFXCreateSession(mfx, 0, &mfx_cpu_decode_session);
|
||||
EXPECT_EQ(MFX_ERR_NONE, sts);
|
||||
|
||||
// create decode engines
|
||||
auto device_selector = decode_accel_policy->get_device_selector();
|
||||
VPLLegacyDecodeEngine decode_engine(std::move(decode_accel_policy));
|
||||
auto sess_ptr = decode_engine.initialize_session(mfx_decode_session,
|
||||
cfg_params_w_accel,
|
||||
data_provider);
|
||||
std::vector<CfgParam> cfg_params_cpu;
|
||||
auto cpu_device_selector = std::make_shared<CfgParamDeviceSelector>(cfg_params_cpu);
|
||||
VPLLegacyDecodeEngine cpu_decode_engine(std::unique_ptr<VPLAccelerationPolicy>{
|
||||
new VPLCPUAccelerationPolicy(cpu_device_selector)});
|
||||
auto cpu_sess_ptr = cpu_decode_engine.initialize_session(mfx_cpu_decode_session,
|
||||
cfg_params_cpu,
|
||||
cpu_data_provider);
|
||||
|
||||
// create VPP preproc engines
|
||||
VPPPreprocDispatcher preproc_dispatcher;
|
||||
preproc_dispatcher.insert_worker<VPPPreprocEngine>(create_accel_policy_from_int(accel, device_selector));
|
||||
preproc_dispatcher.insert_worker<VPPPreprocEngine>(std::unique_ptr<VPLAccelerationPolicy>{
|
||||
new VPLCPUAccelerationPolicy(cpu_device_selector)});
|
||||
|
||||
// launch threads
|
||||
SafeQueue queue;
|
||||
int decoded_number = 1;
|
||||
int cpu_decoded_number = 1;
|
||||
int preproc_number = 0;
|
||||
|
||||
std::thread decode_thread(decode_function, std::ref(decode_engine), sess_ptr,
|
||||
std::ref(queue), std::ref(decoded_number));
|
||||
std::thread cpu_decode_thread(decode_function, std::ref(cpu_decode_engine), cpu_sess_ptr,
|
||||
std::ref(queue), std::ref(cpu_decoded_number));
|
||||
std::thread preproc_thread(multi_source_preproc_function,
|
||||
preproc_dispatcher.size(),
|
||||
std::ref(preproc_dispatcher),
|
||||
std::ref(queue), std::ref(preproc_number),
|
||||
std::cref(required_frame_param),
|
||||
std::cref(opt_roi));
|
||||
|
||||
decode_thread.join();
|
||||
cpu_decode_thread.join();
|
||||
preproc_thread.join();
|
||||
ASSERT_EQ(preproc_number, decoded_number + cpu_decoded_number);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(OneVPL_Source_PreprocDispatcherROI, VPPPreprocDispatcherROIParams,
|
||||
testing::ValuesIn(files_w_roi));
|
||||
|
||||
#endif // __WIN32__
|
||||
} // namespace opencv_test
|
||||
#endif // HAVE_ONEVPL
|
||||
Reference in New Issue
Block a user