Repository for OpenCV's extra modules
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

408 lines
17 KiB

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
using namespace cv;
using namespace cv::cuda;
using namespace cv::cudacodec;
#ifndef HAVE_NVCUVID
Ptr<VideoReader> cv::cudacodec::createVideoReader(const String&, const std::vector<int>&, const VideoReaderInitParams) { throw_no_cuda(); return Ptr<VideoReader>(); }
Ptr<VideoReader> cv::cudacodec::createVideoReader(const Ptr<RawVideoSource>&, const VideoReaderInitParams) { throw_no_cuda(); return Ptr<VideoReader>(); }
#else // HAVE_NVCUVID
void nv12ToBgra(const GpuMat& decodedFrame, GpuMat& outFrame, int width, int height, const bool videoFullRangeFlag, cudaStream_t stream);
bool ValidColorFormat(const ColorFormat colorFormat);
void cvtFromNv12(const GpuMat& decodedFrame, GpuMat& outFrame, int width, int height, const ColorFormat colorFormat, const bool videoFullRangeFlag,
Stream stream)
{
CV_Assert(decodedFrame.cols == width && decodedFrame.rows == height * 1.5f);
if (colorFormat == ColorFormat::BGRA) {
nv12ToBgra(decodedFrame, outFrame, width, height, videoFullRangeFlag, StreamAccessor::getStream(stream));
}
else if (colorFormat == ColorFormat::BGR) {
outFrame.create(height, width, CV_8UC3);
Npp8u* pSrc[2] = { decodedFrame.data, &decodedFrame.data[decodedFrame.step * height] };
NppiSize oSizeROI = { width,height };
#if (CUDART_VERSION < 9200)
CV_Error(Error::StsUnsupportedFormat, "ColorFormat::BGR is not supported until CUDA 9.2, use default ColorFormat::BGRA.");
#elif (CUDART_VERSION < 10100)
cv::cuda::NppStreamHandler h(stream);
if (videoFullRangeFlag)
nppSafeCall(nppiNV12ToBGR_709HDTV_8u_P2C3R(pSrc, decodedFrame.step, outFrame.data, outFrame.step, oSizeROI));
else {
CV_LOG_DEBUG(NULL, "Color reproduction may be inaccurate due CUDA version <= 11.0, for better results upgrade CUDA runtime or try ColorFormat::BGRA.");
nppSafeCall(nppiNV12ToBGR_8u_P2C3R(pSrc, decodedFrame.step, outFrame.data, outFrame.step, oSizeROI));
}
#elif (CUDART_VERSION >= 10100)
NppStreamContext nppStreamCtx;
nppSafeCall(nppGetStreamContext(&nppStreamCtx));
nppStreamCtx.hStream = StreamAccessor::getStream(stream);
if (videoFullRangeFlag)
nppSafeCall(nppiNV12ToBGR_709HDTV_8u_P2C3R_Ctx(pSrc, decodedFrame.step, outFrame.data, outFrame.step, oSizeROI, nppStreamCtx));
else {
#if (CUDART_VERSION < 11000)
CV_LOG_DEBUG(NULL, "Color reproduction may be inaccurate due CUDA version <= 11.0, for better results upgrade CUDA runtime or try ColorFormat::BGRA.");
nppSafeCall(nppiNV12ToBGR_8u_P2C3R_Ctx(pSrc, decodedFrame.step, outFrame.data, outFrame.step, oSizeROI, nppStreamCtx));
#else
nppSafeCall(nppiNV12ToBGR_709CSC_8u_P2C3R_Ctx(pSrc, decodedFrame.step, outFrame.data, outFrame.step, oSizeROI, nppStreamCtx));
#endif
}
#endif
}
else if (colorFormat == ColorFormat::GRAY) {
outFrame.create(height, width, CV_8UC1);
if(videoFullRangeFlag)
cudaSafeCall(cudaMemcpy2DAsync(outFrame.ptr(), outFrame.step, decodedFrame.ptr(), decodedFrame.step, width, height, cudaMemcpyDeviceToDevice, StreamAccessor::getStream(stream)));
else {
cv::cuda::subtract(decodedFrame(Rect(0,0,width,height)), 16, outFrame, noArray(), CV_8U, stream);
cv::cuda::multiply(outFrame, 255.0f / 219.0f, outFrame, 1.0, CV_8U, stream);
}
}
else if (colorFormat == ColorFormat::NV_NV12) {
decodedFrame.copyTo(outFrame, stream);
}
}
using namespace cv::cudacodec::detail;
namespace
{
class VideoReaderImpl : public VideoReader
{
public:
explicit VideoReaderImpl(const Ptr<VideoSource>& source, const int minNumDecodeSurfaces, const bool allowFrameDrop = false , const bool udpSource = false,
const Size targetSz = Size(), const Rect srcRoi = Rect(), const Rect targetRoi = Rect());
~VideoReaderImpl();
bool nextFrame(GpuMat& frame, Stream& stream) CV_OVERRIDE;
FormatInfo format() const CV_OVERRIDE;
bool grab(Stream& stream) CV_OVERRIDE;
bool retrieve(OutputArray frame, const size_t idx) const CV_OVERRIDE;
bool set(const VideoReaderProps propertyId, const double propertyVal) CV_OVERRIDE;
bool set(const ColorFormat colorFormat_) CV_OVERRIDE;
bool get(const VideoReaderProps propertyId, double& propertyVal) const CV_OVERRIDE;
bool getVideoReaderProps(const VideoReaderProps propertyId, double& propertyValOut, double propertyValIn) const CV_OVERRIDE;
bool get(const int propertyId, double& propertyVal) const CV_OVERRIDE;
private:
bool internalGrab(GpuMat& frame, Stream& stream);
Ptr<VideoSource> videoSource_;
Ptr<FrameQueue> frameQueue_ = 0;
Ptr<VideoDecoder> videoDecoder_ = 0;
Ptr<VideoParser> videoParser_ = 0;
CUvideoctxlock lock_;
std::deque< std::pair<CUVIDPARSERDISPINFO, CUVIDPROCPARAMS> > frames_;
std::vector<RawPacket> rawPackets;
GpuMat lastFrame;
static const int decodedFrameIdx = 0;
static const int extraDataIdx = 1;
static const int rawPacketsBaseIdx = 2;
ColorFormat colorFormat = ColorFormat::BGRA;
};
FormatInfo VideoReaderImpl::format() const
{
return videoSource_->format();
}
VideoReaderImpl::VideoReaderImpl(const Ptr<VideoSource>& source, const int minNumDecodeSurfaces, const bool allowFrameDrop, const bool udpSource,
const Size targetSz, const Rect srcRoi, const Rect targetRoi) :
videoSource_(source),
lock_(0)
{
// init context
GpuMat temp(1, 1, CV_8UC1);
temp.release();
CUcontext ctx;
cuSafeCall( cuCtxGetCurrent(&ctx) );
cuSafeCall( cuvidCtxLockCreate(&lock_, ctx) );
frameQueue_.reset(new FrameQueue());
videoDecoder_.reset(new VideoDecoder(videoSource_->format().codec, minNumDecodeSurfaces, targetSz, srcRoi, targetRoi, ctx, lock_));
videoParser_.reset(new VideoParser(videoDecoder_, frameQueue_, allowFrameDrop, udpSource));
videoSource_->setVideoParser(videoParser_);
videoSource_->start();
}
VideoReaderImpl::~VideoReaderImpl()
{
frameQueue_->endDecode();
videoSource_->stop();
}
class VideoCtxAutoLock
{
public:
VideoCtxAutoLock(CUvideoctxlock lock) : m_lock(lock) { cuSafeCall( cuvidCtxLock(m_lock, 0) ); }
~VideoCtxAutoLock() { cuvidCtxUnlock(m_lock, 0); }
private:
CUvideoctxlock m_lock;
};
bool VideoReaderImpl::internalGrab(GpuMat& frame, Stream& stream) {
if (videoParser_->hasError())
CV_Error(Error::StsError, "Parsing/Decoding video source failed, check GPU memory is available and GPU supports hardware decoding.");
if (frames_.empty())
{
CUVIDPARSERDISPINFO displayInfo;
rawPackets.clear();
for (;;)
{
if (frameQueue_->dequeue(displayInfo, rawPackets))
break;
if (videoParser_->hasError())
CV_Error(Error::StsError, "Parsing/Decoding video source failed, check GPU memory is available and GPU supports hardware decoding.");
if (frameQueue_->isEndOfDecode())
return false;
// Wait a bit
Thread::sleep(1);
}
bool isProgressive = displayInfo.progressive_frame != 0;
const int num_fields = isProgressive ? 1 : 2 + displayInfo.repeat_first_field;
videoSource_->updateFormat(videoDecoder_->format());
for (int active_field = 0; active_field < num_fields; ++active_field)
{
CUVIDPROCPARAMS videoProcParams;
std::memset(&videoProcParams, 0, sizeof(CUVIDPROCPARAMS));
videoProcParams.progressive_frame = displayInfo.progressive_frame;
videoProcParams.second_field = active_field;
videoProcParams.top_field_first = displayInfo.top_field_first;
videoProcParams.unpaired_field = (num_fields == 1);
videoProcParams.output_stream = StreamAccessor::getStream(stream);
frames_.push_back(std::make_pair(displayInfo, videoProcParams));
}
}
if (frames_.empty())
return false;
std::pair<CUVIDPARSERDISPINFO, CUVIDPROCPARAMS> frameInfo = frames_.front();
frames_.pop_front();
{
VideoCtxAutoLock autoLock(lock_);
// map decoded video frame to CUDA surface
GpuMat decodedFrame = videoDecoder_->mapFrame(frameInfo.first.picture_index, frameInfo.second);
cvtFromNv12(decodedFrame, frame, videoDecoder_->targetWidth(), videoDecoder_->targetHeight(), colorFormat, videoDecoder_->format().videoFullRangeFlag, stream);
// unmap video frame
// unmapFrame() synchronizes with the VideoDecode API (ensures the frame has finished decoding)
videoDecoder_->unmapFrame(decodedFrame);
}
// release the frame, so it can be re-used in decoder
if (frames_.empty())
frameQueue_->releaseFrame(frameInfo.first);
return true;
}
bool VideoReaderImpl::grab(Stream& stream) {
return internalGrab(lastFrame, stream);
}
bool VideoReaderImpl::retrieve(OutputArray frame, const size_t idx) const {
if (idx == decodedFrameIdx) {
if (!frame.isGpuMat())
CV_Error(Error::StsUnsupportedFormat, "Decoded frame is stored on the device and must be retrieved using a cv::cuda::GpuMat");
frame.getGpuMatRef() = lastFrame;
}
else if (idx == extraDataIdx) {
if (!frame.isMat())
CV_Error(Error::StsUnsupportedFormat, "Extra data is stored on the host and must be retrieved using a cv::Mat");
videoSource_->getExtraData(frame.getMatRef());
}
else{
if (idx >= rawPacketsBaseIdx && idx < rawPacketsBaseIdx + rawPackets.size()) {
if (!frame.isMat())
CV_Error(Error::StsUnsupportedFormat, "Raw data is stored on the host and must be retrieved using a cv::Mat");
const size_t i = idx - rawPacketsBaseIdx;
Mat tmp(1, rawPackets.at(i).Size(), CV_8UC1, const_cast<unsigned char*>(rawPackets.at(i).Data()), rawPackets.at(i).Size());
frame.getMatRef() = tmp;
}
}
return !frame.empty();
}
bool VideoReaderImpl::set(const VideoReaderProps propertyId, const double propertyVal) {
switch (propertyId) {
case VideoReaderProps::PROP_RAW_MODE :
videoSource_->SetRawMode(static_cast<bool>(propertyVal));
return true;
}
return false;
}
bool ValidColorFormat(const ColorFormat colorFormat) {
if (colorFormat == ColorFormat::BGRA || colorFormat == ColorFormat::BGR || colorFormat == ColorFormat::GRAY || colorFormat == ColorFormat::NV_NV12)
return true;
return false;
}
bool VideoReaderImpl::set(const ColorFormat colorFormat_) {
if (!ValidColorFormat(colorFormat_)) return false;
colorFormat = colorFormat_;
return true;
}
bool VideoReaderImpl::get(const VideoReaderProps propertyId, double& propertyVal) const {
switch (propertyId)
{
case VideoReaderProps::PROP_DECODED_FRAME_IDX:
propertyVal = decodedFrameIdx;
return true;
case VideoReaderProps::PROP_EXTRA_DATA_INDEX:
propertyVal = extraDataIdx;
return true;
case VideoReaderProps::PROP_RAW_PACKAGES_BASE_INDEX:
if (videoSource_->RawModeEnabled()) {
propertyVal = rawPacketsBaseIdx;
return true;
}
else
break;
case VideoReaderProps::PROP_NUMBER_OF_RAW_PACKAGES_SINCE_LAST_GRAB:
propertyVal = rawPackets.size();
return true;
case VideoReaderProps::PROP_RAW_MODE:
propertyVal = videoSource_->RawModeEnabled();
return true;
case VideoReaderProps::PROP_LRF_HAS_KEY_FRAME: {
const int iPacket = propertyVal - rawPacketsBaseIdx;
if (videoSource_->RawModeEnabled() && iPacket >= 0 && iPacket < rawPackets.size()) {
propertyVal = rawPackets.at(iPacket).ContainsKeyFrame();
return true;
}
else
break;
}
case VideoReaderProps::PROP_ALLOW_FRAME_DROP:
propertyVal = videoParser_->allowFrameDrops();
return true;
case VideoReaderProps::PROP_UDP_SOURCE:
propertyVal = videoParser_->udpSource();
return true;
case VideoReaderProps::PROP_COLOR_FORMAT:
propertyVal = static_cast<double>(colorFormat);
return true;
default:
break;
}
return false;
}
bool VideoReaderImpl::getVideoReaderProps(const VideoReaderProps propertyId, double& propertyValOut, double propertyValIn) const {
double propertyValInOut = propertyValIn;
const bool ret = get(propertyId, propertyValInOut);
propertyValOut = propertyValInOut;
return ret;
}
bool VideoReaderImpl::get(const int propertyId, double& propertyVal) const {
return videoSource_->get(propertyId, propertyVal);
}
bool VideoReaderImpl::nextFrame(GpuMat& frame, Stream& stream)
{
if (!internalGrab(frame, stream))
return false;
return true;
}
}
Ptr<VideoReader> cv::cudacodec::createVideoReader(const String& filename, const std::vector<int>& sourceParams, const VideoReaderInitParams params)
{
CV_Assert(!filename.empty());
Ptr<VideoSource> videoSource;
try
{
// prefer ffmpeg to cuvidGetSourceVideoFormat() which doesn't always return the corrct raw pixel format
Ptr<RawVideoSource> source(new FFmpegVideoSource(filename, sourceParams));
videoSource.reset(new RawVideoSourceWrapper(source, params.rawMode));
}
catch (...)
{
if (sourceParams.size()) throw;
videoSource.reset(new CuvidVideoSource(filename));
}
return makePtr<VideoReaderImpl>(videoSource, params.minNumDecodeSurfaces, params.allowFrameDrop, params.udpSource, params.targetSz,
params.srcRoi, params.targetRoi);
}
Ptr<VideoReader> cv::cudacodec::createVideoReader(const Ptr<RawVideoSource>& source, const VideoReaderInitParams params)
{
Ptr<VideoSource> videoSource(new RawVideoSourceWrapper(source, params.rawMode));
return makePtr<VideoReaderImpl>(videoSource, params.minNumDecodeSurfaces, params.allowFrameDrop, params.udpSource, params.targetSz,
params.srcRoi, params.targetRoi);
}
#endif // HAVE_NVCUVID