From 9a81ff731542a3f6fa2263d990424528b42f9c19 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 29 Mar 2019 17:52:22 +0300 Subject: [PATCH] Backported AVFoundation fixes from master branch --- modules/videoio/src/cap_avfoundation_mac.mm | 188 +++++++++++--------- modules/videoio/src/cap_mfx_writer.cpp | 6 + modules/videoio/test/test_mfx.cpp | 2 +- modules/videoio/test/test_video_io.cpp | 2 - 4 files changed, 109 insertions(+), 89 deletions(-) diff --git a/modules/videoio/src/cap_avfoundation_mac.mm b/modules/videoio/src/cap_avfoundation_mac.mm index 651887c4ed..7a04b94948 100644 --- a/modules/videoio/src/cap_avfoundation_mac.mm +++ b/modules/videoio/src/cap_avfoundation_mac.mm @@ -43,6 +43,7 @@ #include "precomp.hpp" #include "opencv2/imgproc.hpp" #include +#include #import /********************** Declaration of class headers ************************/ @@ -177,12 +178,14 @@ private: class CvVideoWriter_AVFoundation : public CvVideoWriter { public: - CvVideoWriter_AVFoundation(const char* filename, int fourcc, - double fps, CvSize frame_size, - int is_color=1); + CvVideoWriter_AVFoundation(const std::string &filename, int fourcc, double fps, CvSize frame_size, int is_color); ~CvVideoWriter_AVFoundation(); bool writeFrame(const IplImage* image) CV_OVERRIDE; int getCaptureDomain() const CV_OVERRIDE { return cv::CAP_AVFOUNDATION; } + bool isOpened() const + { + return is_good; + } private: IplImage* argbimage; @@ -197,6 +200,7 @@ class CvVideoWriter_AVFoundation : public CvVideoWriter { CvSize movieSize; int movieColor; unsigned long mFrameNum; + bool is_good; }; /****************** Implementation of interface functions ********************/ @@ -221,7 +225,13 @@ CvCapture* cvCreateCameraCapture_AVFoundation(int index ) { CvVideoWriter* cvCreateVideoWriter_AVFoundation(const char* filename, int fourcc, double fps, CvSize frame_size, int is_color) { - return new CvVideoWriter_AVFoundation(filename, fourcc, fps, frame_size,is_color); + CvVideoWriter_AVFoundation* wrt = new CvVideoWriter_AVFoundation(filename, fourcc, fps, frame_size, is_color); + if (wrt->isOpened()) + { + return wrt; + } + delete wrt; + return NULL; } /********************** Implementation of Classes ****************************/ @@ -305,6 +315,28 @@ void CvCaptureCAM::stopCaptureDevice() { int CvCaptureCAM::startCaptureDevice(int cameraNum) { NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init]; +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 + AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + if (status == AVAuthorizationStatusDenied) + { + fprintf(stderr, "OpenCV: camera access has been denied. Either run 'tccutil reset Camera' " + "command in same terminal to reset application authorization status, " + "either modify 'System Preferences -> Security & Privacy -> Camera' " + "settings for your application.\n"); + [localpool drain]; + return 0; + } + else if (status != AVAuthorizationStatusAuthorized) + { + fprintf(stderr, "OpenCV: not authorized to capture video (status %ld), requesting...\n", status); + // TODO: doesn't work via ssh + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL) { /* we don't care */}]; + // we do not wait for completion + [localpool drain]; + return 0; + } +#endif + // get capture device NSArray *devices = [[AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo] arrayByAddingObjectsFromArray:[AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]]; @@ -1096,38 +1128,20 @@ bool CvCaptureFile::setProperty(int property_id, double value) { *****************************************************************************/ -CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int fourcc, - double fps, CvSize frame_size, - int is_color) { - +CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const std::string &filename, int fourcc, double fps, CvSize frame_size, int is_color) + : argbimage(nil), mMovieWriter(nil), mMovieWriterInput(nil), mMovieWriterAdaptor(nil), path(nil), + codec(nil), fileType(nil), mMovieFPS(fps), movieSize(frame_size), movieColor(is_color), mFrameNum(0), + is_good(true) +{ + if (mMovieFPS <= 0 || movieSize.width <= 0 || movieSize.height <= 0) + { + is_good = false; + return; + } NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - mFrameNum = 0; - mMovieFPS = fps; - movieSize = frame_size; - movieColor = is_color; argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4); - path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; - - - /* - AVFileTypeQuickTimeMovie - UTI for the QuickTime movie file format. - The value of this UTI is com.apple.quicktime-movie. Files are identified with the .mov and .qt extensions. - - AVFileTypeMPEG4 - UTI for the MPEG-4 file format. - The value of this UTI is public.mpeg-4. Files are identified with the .mp4 extension. - - AVFileTypeAppleM4V - UTI for the iTunes video file format. - The value of this UTI is com.apple.mpeg-4-video. Files are identified with the .m4v extension. - - AVFileType3GPP - UTI for the 3GPP file format. - The value of this UTI is public.3gpp. Files are identified with the .3gp, .3gpp, and .sdv extensions. - */ + path = [[[NSString stringWithUTF8String:filename.c_str()] stringByExpandingTildeInPath] retain]; NSString *fileExt =[[[path pathExtension] lowercaseString] copy]; if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]){ @@ -1136,12 +1150,8 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int fileType = [AVFileTypeMPEG4 copy]; }else if ([fileExt isEqualToString:@"m4v"]){ fileType = [AVFileTypeAppleM4V copy]; -#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - }else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"] ){ - fileType = [AVFileType3GPP copy]; -#endif } else{ - fileType = [AVFileTypeMPEG4 copy]; //default mp4 + is_good = false; } [fileExt release]; @@ -1153,8 +1163,7 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int cc[4] = 0; int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); if (cc2!=fourcc) { - fprintf(stderr, "OpenCV: Didn't properly encode FourCC. Expected 0x%08X but got 0x%08X.\n", fourcc, cc2); - //exception; + is_good = false; } // Two codec supported AVVideoCodecH264 AVVideoCodecJPEG @@ -1165,59 +1174,59 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int }else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){ codec = [AVVideoCodecH264 copy]; }else{ - codec = [AVVideoCodecH264 copy]; // default canonical H264. - + is_good = false; } //NSLog(@"Path: %@", path); - NSError *error = nil; - + if (is_good) + { + NSError *error = nil; - // Make sure the file does not already exist. Necessary to overwirte?? - /* - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:path]){ - [fileManager removeItemAtPath:path error:&error]; - } - */ - // Wire the writer: - // Supported file types: - // AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP + // Make sure the file does not already exist. Necessary to overwirte?? + /* + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:path]){ + [fileManager removeItemAtPath:path error:&error]; + } + */ - mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] - fileType:fileType - error:&error]; - //NSParameterAssert(mMovieWriter); + // Wire the writer: + // Supported file types: + // AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP - NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: - codec, AVVideoCodecKey, - [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey, - [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey, - nil]; + mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] + fileType:fileType + error:&error]; + //NSParameterAssert(mMovieWriter); - mMovieWriterInput = [[AVAssetWriterInput - assetWriterInputWithMediaType:AVMediaTypeVideo - outputSettings:videoSettings] retain]; + NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: + codec, AVVideoCodecKey, + [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey, + [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey, + nil]; - //NSParameterAssert(mMovieWriterInput); - //NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]); + mMovieWriterInput = [[AVAssetWriterInput + assetWriterInputWithMediaType:AVMediaTypeVideo + outputSettings:videoSettings] retain]; - [mMovieWriter addInput:mMovieWriterInput]; + //NSParameterAssert(mMovieWriterInput); + //NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]); - mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil]; + [mMovieWriter addInput:mMovieWriterInput]; + mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil]; - //Start a session: - [mMovieWriter startWriting]; - [mMovieWriter startSessionAtSourceTime:kCMTimeZero]; + //Start a session: + [mMovieWriter startWriting]; + [mMovieWriter startSessionAtSourceTime:kCMTimeZero]; - if(mMovieWriter.status == AVAssetWriterStatusFailed){ - NSLog(@"AVF: AVAssetWriter status: %@", [mMovieWriter.error localizedDescription]); - // TODO: error handling, cleanup. Throw execption? - // return; + if(mMovieWriter.status == AVAssetWriterStatusFailed){ + NSLog(@"AVF: AVAssetWriter status: %@", [mMovieWriter.error localizedDescription]); + is_good = false; + } } [localpool drain]; @@ -1227,15 +1236,22 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - [mMovieWriterInput markAsFinished]; - [mMovieWriter finishWriting]; - [mMovieWriter release]; - [mMovieWriterInput release]; - [mMovieWriterAdaptor release]; - [path release]; - [codec release]; - [fileType release]; - cvReleaseImage(&argbimage); + if (mMovieWriterInput && mMovieWriter && mMovieWriterAdaptor) + { + [mMovieWriterInput markAsFinished]; + [mMovieWriter finishWriting]; + [mMovieWriter release]; + [mMovieWriterInput release]; + [mMovieWriterAdaptor release]; + } + if (path) + [path release]; + if (codec) + [codec release]; + if (fileType) + [fileType release]; + if (argbimage) + cvReleaseImage(&argbimage); [localpool drain]; diff --git a/modules/videoio/src/cap_mfx_writer.cpp b/modules/videoio/src/cap_mfx_writer.cpp index a8e2406156..ed3c30ad08 100644 --- a/modules/videoio/src/cap_mfx_writer.cpp +++ b/modules/videoio/src/cap_mfx_writer.cpp @@ -40,6 +40,12 @@ VideoWriter_IntelMFX::VideoWriter_IntelMFX(const String &filename, int _fourcc, return; } + if (fps <= 0) + { + MSG(cerr << "MFX: Invalid FPS passed to encoder" << endl); + return; + } + // Init device and session deviceHandler = createDeviceHandler(); session = new MFXVideoSession(); diff --git a/modules/videoio/test/test_mfx.cpp b/modules/videoio/test/test_mfx.cpp index caa3fe94b6..f739cbda17 100644 --- a/modules/videoio/test/test_mfx.cpp +++ b/modules/videoio/test/test_mfx.cpp @@ -35,7 +35,7 @@ TEST(Videoio_MFX, write_invalid) ASSERT_NO_THROW(res = writer.open(String(), CAP_INTEL_MFX, VideoWriter::fourcc('H', '2', '6', '4'), 1, Size(640, 480), true)); EXPECT_FALSE(res); EXPECT_FALSE(writer.isOpened()); - ASSERT_ANY_THROW(res = writer.open(filename, CAP_INTEL_MFX, VideoWriter::fourcc('H', '2', '6', '4'), 0, Size(640, 480), true)); + ASSERT_NO_THROW(res = writer.open(filename, CAP_INTEL_MFX, VideoWriter::fourcc('H', '2', '6', '4'), 0, Size(640, 480), true)); EXPECT_FALSE(res); EXPECT_FALSE(writer.isOpened()); diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index f8c27c8253..98b293e09a 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -419,8 +419,6 @@ static Ext_Fourcc_PSNR synthetic_params[] = { makeParam("mp4", "MJPG", 30.f, CAP_AVFOUNDATION), makeParam("m4v", "H264", 30.f, CAP_AVFOUNDATION), makeParam("m4v", "MJPG", 30.f, CAP_AVFOUNDATION), - makeParam("3gp", "H264", 30.f, CAP_AVFOUNDATION), - makeParam("3gp", "MJPG", 30.f, CAP_AVFOUNDATION), #endif #ifdef HAVE_FFMPEG