mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -1155,228 +1155,198 @@ CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int
|
||||
double fps, const cv::Size& frame_size,
|
||||
int is_color) {
|
||||
|
||||
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
||||
@autoreleasepool {
|
||||
frameCount = 0;
|
||||
movieFPS = fps;
|
||||
movieSize = frame_size;
|
||||
movieColor = is_color;
|
||||
argbimage = cv::Mat(movieSize, CV_8UC4);
|
||||
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.
|
||||
|
||||
frameCount = 0;
|
||||
movieFPS = fps;
|
||||
movieSize = frame_size;
|
||||
movieColor = is_color;
|
||||
argbimage = cv::Mat(movieSize, CV_8UC4);
|
||||
path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain];
|
||||
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.
|
||||
|
||||
/*
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
NSString *fileExt =[[[path pathExtension] lowercaseString] copy];
|
||||
if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]){
|
||||
fileType = [AVFileTypeQuickTimeMovie copy];
|
||||
}else if ([fileExt isEqualToString:@"mp4"]){
|
||||
fileType = [AVFileTypeMPEG4 copy];
|
||||
}else if ([fileExt isEqualToString:@"m4v"]){
|
||||
fileType = [AVFileTypeAppleM4V copy];
|
||||
NSString *fileExt = [[[path pathExtension] lowercaseString] copy];
|
||||
if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]) {
|
||||
fileType = [AVFileTypeQuickTimeMovie copy];
|
||||
} else if ([fileExt isEqualToString:@"mp4"]) {
|
||||
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];
|
||||
} else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"]) {
|
||||
fileType = [AVFileType3GPP copy];
|
||||
#endif
|
||||
} else{
|
||||
fileType = [AVFileTypeMPEG4 copy]; //default mp4
|
||||
}
|
||||
[fileExt release];
|
||||
} else {
|
||||
fileType = [AVFileTypeMPEG4 copy]; //default mp4
|
||||
}
|
||||
[fileExt release];
|
||||
|
||||
char cc[5];
|
||||
cc[0] = fourcc & 255;
|
||||
cc[1] = (fourcc >> 8) & 255;
|
||||
cc[2] = (fourcc >> 16) & 255;
|
||||
cc[3] = (fourcc >> 24) & 255;
|
||||
cc[4] = 0;
|
||||
int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]);
|
||||
if (cc2!=fourcc) {
|
||||
std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc
|
||||
<< " but got " << cc2 << "." << std::endl;
|
||||
//exception;
|
||||
}
|
||||
char cc[5];
|
||||
cc[0] = fourcc & 255;
|
||||
cc[1] = (fourcc >> 8) & 255;
|
||||
cc[2] = (fourcc >> 16) & 255;
|
||||
cc[3] = (fourcc >> 24) & 255;
|
||||
cc[4] = 0;
|
||||
int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]);
|
||||
if (cc2 != fourcc) {
|
||||
std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc
|
||||
<< " but got " << cc2 << "." << std::endl;
|
||||
}
|
||||
|
||||
// Three codec supported AVVideoCodecTypeH264 AVVideoCodecTypeJPEG AVVideoCodecTypeHEVC
|
||||
// On iPhone 3G H264 is not supported.
|
||||
if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') ||
|
||||
fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g')){
|
||||
codec = [AVVideoCodecTypeJPEG copy]; // Use JPEG codec if specified, otherwise H264
|
||||
}else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){
|
||||
codec = [AVVideoCodecTypeH264 copy];
|
||||
// Three codec supported AVVideoCodecTypeH264 AVVideoCodecTypeJPEG AVVideoCodecTypeHEVC
|
||||
// On iPhone 3G H264 is not supported.
|
||||
if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') ||
|
||||
fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g')) {
|
||||
codec = [AVVideoCodecTypeJPEG copy]; // Use JPEG codec if specified, otherwise H264
|
||||
} else if (fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')) {
|
||||
codec = [AVVideoCodecTypeH264 copy];
|
||||
// Available since iOS 11
|
||||
#if TARGET_OS_VISION || (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
|
||||
}else if(fourcc == CV_FOURCC('H','2','6','5') || fourcc == CV_FOURCC('h','v','c','1') ||
|
||||
fourcc == CV_FOURCC('H','E','V','C') || fourcc == CV_FOURCC('h','e','v','c')){
|
||||
if (@available(iOS 11, *)) {
|
||||
codec = [AVVideoCodecTypeHEVC copy];
|
||||
} else {
|
||||
codec = [AVVideoCodecTypeH264 copy];
|
||||
}
|
||||
} else if (fourcc == CV_FOURCC('H','2','6','5') || fourcc == CV_FOURCC('h','v','c','1') ||
|
||||
fourcc == CV_FOURCC('H','E','V','C') || fourcc == CV_FOURCC('h','e','v','c')) {
|
||||
if (@available(iOS 11, *)) {
|
||||
codec = [AVVideoCodecTypeHEVC copy];
|
||||
} else {
|
||||
codec = [AVVideoCodecTypeH264 copy];
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
codec = [AVVideoCodecTypeH264 copy]; // default canonical H264.
|
||||
} else {
|
||||
codec = [AVVideoCodecTypeH264 copy]; // default canonical H264.
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
// Wire the writer:
|
||||
// Supported file types:
|
||||
// AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP
|
||||
|
||||
mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
|
||||
fileType:fileType
|
||||
error:&error];
|
||||
|
||||
NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
codec, AVVideoCodecKey,
|
||||
[NSNumber numberWithInt:movieSize.width], AVVideoWidthKey,
|
||||
[NSNumber numberWithInt:movieSize.height], AVVideoHeightKey,
|
||||
nil];
|
||||
|
||||
mMovieWriterInput = [[AVAssetWriterInput
|
||||
assetWriterInputWithMediaType:AVMediaTypeVideo
|
||||
outputSettings:videoSettings] retain];
|
||||
|
||||
[mMovieWriter addInput:mMovieWriterInput];
|
||||
|
||||
mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil];
|
||||
|
||||
//Start a session:
|
||||
[mMovieWriter startWriting];
|
||||
[mMovieWriter startSessionAtSourceTime:kCMTimeZero];
|
||||
|
||||
if (mMovieWriter.status == AVAssetWriterStatusFailed) {
|
||||
NSLog(@"%@", [mMovieWriter.error localizedDescription]);
|
||||
// TODO: error handling, cleanup. Throw exception?
|
||||
}
|
||||
}
|
||||
|
||||
//NSLog(@"Path: %@", path);
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
|
||||
// Make sure the file does not already exist. Necessary to overwrite??
|
||||
/*
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if ([fileManager fileExistsAtPath:path]){
|
||||
[fileManager removeItemAtPath:path error:&error];
|
||||
}
|
||||
*/
|
||||
|
||||
// Wire the writer:
|
||||
// Supported file types:
|
||||
// AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP
|
||||
|
||||
mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
|
||||
fileType:fileType
|
||||
error:&error];
|
||||
//NSParameterAssert(mMovieWriter);
|
||||
|
||||
NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
codec, AVVideoCodecKey,
|
||||
[NSNumber numberWithInt:movieSize.width], AVVideoWidthKey,
|
||||
[NSNumber numberWithInt:movieSize.height], AVVideoHeightKey,
|
||||
nil];
|
||||
|
||||
mMovieWriterInput = [[AVAssetWriterInput
|
||||
assetWriterInputWithMediaType:AVMediaTypeVideo
|
||||
outputSettings:videoSettings] retain];
|
||||
|
||||
//NSParameterAssert(mMovieWriterInput);
|
||||
//NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]);
|
||||
|
||||
[mMovieWriter addInput:mMovieWriterInput];
|
||||
|
||||
mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil];
|
||||
|
||||
|
||||
//Start a session:
|
||||
[mMovieWriter startWriting];
|
||||
[mMovieWriter startSessionAtSourceTime:kCMTimeZero];
|
||||
|
||||
|
||||
if(mMovieWriter.status == AVAssetWriterStatusFailed){
|
||||
NSLog(@"%@", [mMovieWriter.error localizedDescription]);
|
||||
// TODO: error handling, cleanup. Throw exception?
|
||||
// return;
|
||||
}
|
||||
|
||||
[localpool drain];
|
||||
}
|
||||
|
||||
|
||||
CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() {
|
||||
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
[mMovieWriterInput markAsFinished];
|
||||
[mMovieWriter finishWritingWithCompletionHandler:^() {
|
||||
[mMovieWriter release];
|
||||
[mMovieWriterInput release];
|
||||
[mMovieWriterAdaptor release];
|
||||
[path release];
|
||||
[codec release];
|
||||
[fileType release];
|
||||
argbimage.release();
|
||||
|
||||
[localpool drain];
|
||||
}];
|
||||
@autoreleasepool {
|
||||
[mMovieWriterInput markAsFinished];
|
||||
[mMovieWriter finishWritingWithCompletionHandler:^() {
|
||||
@autoreleasepool {
|
||||
[mMovieWriter release];
|
||||
[mMovieWriterInput release];
|
||||
[mMovieWriterAdaptor release];
|
||||
[path release];
|
||||
[codec release];
|
||||
[fileType release];
|
||||
argbimage.release();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
void CvVideoWriter_AVFoundation::write(cv::InputArray image) {
|
||||
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
||||
@autoreleasepool {
|
||||
// writer status check
|
||||
if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting) {
|
||||
NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ...");
|
||||
NSLog(@"mMovieWriter.status: %d. Error: %@", (int)mMovieWriter.status, [mMovieWriter.error localizedDescription]);
|
||||
return;
|
||||
}
|
||||
|
||||
// writer status check
|
||||
if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting ) {
|
||||
NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ...");
|
||||
NSLog(@"mMovieWriter.status: %d. Error: %@", (int)mMovieWriter.status, [mMovieWriter.error localizedDescription]);
|
||||
[localpool drain];
|
||||
return;
|
||||
BOOL success = FALSE;
|
||||
|
||||
if (image.size().height != movieSize.height || image.size().width != movieSize.width) {
|
||||
std::cout << "Frame size does not match video size." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (movieColor) {
|
||||
cv::cvtColor(image, argbimage, cv::COLOR_BGR2BGRA);
|
||||
} else {
|
||||
cv::cvtColor(image, argbimage, cv::COLOR_GRAY2BGRA);
|
||||
}
|
||||
|
||||
//IplImage -> CGImage conversion
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
NSData *nsData = [NSData dataWithBytes:argbimage.data length:argbimage.total() * argbimage.elemSize()];
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData);
|
||||
CGImageRef cgImage = CGImageCreate(argbimage.size().width, argbimage.size().height,
|
||||
8, 32, argbimage.step[0],
|
||||
colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault,
|
||||
provider, NULL, false, kCGRenderingIntentDefault);
|
||||
|
||||
//CGImage -> CVPixelBufferRef conversion
|
||||
CVPixelBufferRef pixelBuffer = NULL;
|
||||
CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
|
||||
int status = CVPixelBufferCreateWithBytes(NULL,
|
||||
movieSize.width,
|
||||
movieSize.height,
|
||||
kCVPixelFormatType_32BGRA,
|
||||
(void*)CFDataGetBytePtr(cfData),
|
||||
CGImageGetBytesPerRow(cgImage),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&pixelBuffer);
|
||||
if (status == kCVReturnSuccess) {
|
||||
success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer
|
||||
withPresentationTime:CMTimeMake(frameCount, movieFPS)];
|
||||
}
|
||||
|
||||
//cleanup
|
||||
CFRelease(cfData);
|
||||
CVPixelBufferRelease(pixelBuffer);
|
||||
CGImageRelease(cgImage);
|
||||
CGDataProviderRelease(provider);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
if (success) {
|
||||
frameCount++;
|
||||
return;
|
||||
} else {
|
||||
NSLog(@"Frame appendPixelBuffer failed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL success = FALSE;
|
||||
|
||||
if (image.size().height!=movieSize.height || image.size().width!=movieSize.width){
|
||||
std::cout<<"Frame size does not match video size."<<std::endl;
|
||||
[localpool drain];
|
||||
return;
|
||||
}
|
||||
|
||||
if (movieColor) {
|
||||
//assert(image->nChannels == 3);
|
||||
cv::cvtColor(image, argbimage, cv::COLOR_BGR2BGRA);
|
||||
}else{
|
||||
//assert(image->nChannels == 1);
|
||||
cv::cvtColor(image, argbimage, cv::COLOR_GRAY2BGRA);
|
||||
}
|
||||
//IplImage -> CGImage conversion
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
NSData *nsData = [NSData dataWithBytes:argbimage.data length:argbimage.total() * argbimage.elemSize()];
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData);
|
||||
CGImageRef cgImage = CGImageCreate(argbimage.size().width, argbimage.size().height,
|
||||
8, 32, argbimage.step[0],
|
||||
colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault,
|
||||
provider, NULL, false, kCGRenderingIntentDefault);
|
||||
|
||||
//CGImage -> CVPixelBufferRef conversion
|
||||
CVPixelBufferRef pixelBuffer = NULL;
|
||||
CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
|
||||
int status = CVPixelBufferCreateWithBytes(NULL,
|
||||
movieSize.width,
|
||||
movieSize.height,
|
||||
kCVPixelFormatType_32BGRA,
|
||||
(void*)CFDataGetBytePtr(cfData),
|
||||
CGImageGetBytesPerRow(cgImage),
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&pixelBuffer);
|
||||
if(status == kCVReturnSuccess){
|
||||
success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer
|
||||
withPresentationTime:CMTimeMake(frameCount, movieFPS)];
|
||||
}
|
||||
|
||||
//cleanup
|
||||
CFRelease(cfData);
|
||||
CVPixelBufferRelease(pixelBuffer);
|
||||
CGImageRelease(cgImage);
|
||||
CGDataProviderRelease(provider);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
[localpool drain];
|
||||
|
||||
if (success) {
|
||||
frameCount ++;
|
||||
//NSLog(@"Frame #%d", frameCount);
|
||||
return;
|
||||
}else{
|
||||
NSLog(@"Frame appendPixelBuffer failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
#pragma clang diagnostic pop
|
||||
@@ -2394,7 +2394,7 @@ int videoInput::getVideoPropertyFromCV(int cv_property){
|
||||
case CAP_PROP_MONOCHROME:
|
||||
return VideoProcAmp_ColorEnable;
|
||||
|
||||
case CAP_PROP_WHITE_BALANCE_BLUE_U:
|
||||
case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE:
|
||||
return VideoProcAmp_WhiteBalance;
|
||||
|
||||
case cv::VideoCaptureProperties::CAP_PROP_AUTO_WB:
|
||||
@@ -3422,7 +3422,7 @@ double VideoCapture_DShow::getProperty(int propIdx) const
|
||||
case CAP_PROP_SHARPNESS:
|
||||
case CAP_PROP_GAMMA:
|
||||
case CAP_PROP_MONOCHROME:
|
||||
case CAP_PROP_WHITE_BALANCE_BLUE_U:
|
||||
case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE:
|
||||
case CAP_PROP_BACKLIGHT:
|
||||
case CAP_PROP_GAIN:
|
||||
if (g_VI.getVideoSettingFilter(m_index, g_VI.getVideoPropertyFromCV(propIdx), min_value, max_value, stepping_delta, current_value, flags, defaultValue))
|
||||
@@ -3589,7 +3589,7 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal)
|
||||
else
|
||||
flags = VideoProcAmp_Flags_Manual;
|
||||
break;
|
||||
case CAP_PROP_WHITE_BALANCE_BLUE_U:
|
||||
case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE:
|
||||
flags = VideoProcAmp_Flags_Manual;
|
||||
break;
|
||||
}
|
||||
@@ -3604,7 +3604,7 @@ bool VideoCapture_DShow::setProperty(int propIdx, double propVal)
|
||||
case CAP_PROP_SHARPNESS:
|
||||
case CAP_PROP_GAMMA:
|
||||
case CAP_PROP_MONOCHROME:
|
||||
case CAP_PROP_WHITE_BALANCE_BLUE_U:
|
||||
case cv::VideoCaptureProperties::CAP_PROP_WB_TEMPERATURE:
|
||||
case cv::VideoCaptureProperties::CAP_PROP_AUTO_WB:
|
||||
case CAP_PROP_BACKLIGHT:
|
||||
case CAP_PROP_GAIN:
|
||||
|
||||
@@ -524,6 +524,17 @@ inline static std::string _opencv_ffmpeg_get_error_string(int error_code)
|
||||
return std::string("Unknown error");
|
||||
}
|
||||
|
||||
static inline int64_t to_avtb(int64_t ts, AVRational tb)
|
||||
{
|
||||
return av_rescale_q(ts, tb, AV_TIME_BASE_Q);
|
||||
}
|
||||
|
||||
static inline int64_t from_avtb(int64_t ts_avtb, AVRational tb)
|
||||
{
|
||||
return av_rescale_q(ts_avtb, AV_TIME_BASE_Q, tb);
|
||||
}
|
||||
|
||||
|
||||
struct CvCapture_FFMPEG
|
||||
{
|
||||
bool open(const char* filename, int index, const Ptr<IStreamReader>& stream, const VideoCaptureParameters& params);
|
||||
@@ -563,6 +574,10 @@ struct CvCapture_FFMPEG
|
||||
int64_t pts_in_fps_time_base;
|
||||
int64_t dts_delay_in_fps_time_base;
|
||||
|
||||
/// Timestamp offset in AV_TIME_BASE units for normalization
|
||||
int64_t ts_offset_avtb = 0;
|
||||
bool ts_offset_decided = false;
|
||||
|
||||
AVIOContext * avio_context;
|
||||
|
||||
AVPacket packet;
|
||||
@@ -623,6 +638,8 @@ void CvCapture_FFMPEG::init()
|
||||
picture_pts = AV_NOPTS_VALUE_;
|
||||
pts_in_fps_time_base = 0;
|
||||
dts_delay_in_fps_time_base = 0;
|
||||
ts_offset_avtb = 0;
|
||||
ts_offset_decided = false;
|
||||
first_frame_number = -1;
|
||||
memset( &rgb_picture, 0, sizeof(rgb_picture) );
|
||||
memset( &frame, 0, sizeof(frame) );
|
||||
@@ -1705,15 +1722,74 @@ bool CvCapture_FFMPEG::grabFrame()
|
||||
if (picture_pts == AV_NOPTS_VALUE_) {
|
||||
int64_t dts = 0;
|
||||
if (!rawMode) {
|
||||
picture_pts = picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_ && picture->CV_FFMPEG_PTS_FIELD != 0 ? picture->CV_FFMPEG_PTS_FIELD : picture->pkt_dts;
|
||||
if(frame_number == 0) dts = picture->pkt_dts;
|
||||
}
|
||||
else {
|
||||
const AVPacket& packet_raw = packet.data != 0 ? packet : packet_filtered;
|
||||
picture_pts = packet_raw.pts != AV_NOPTS_VALUE_ && packet_raw.pts != 0 ? packet_raw.pts : packet_raw.dts;
|
||||
picture_pts = (picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_)
|
||||
? picture->CV_FFMPEG_PTS_FIELD
|
||||
: picture->pkt_dts;
|
||||
if (frame_number == 0) dts = picture->pkt_dts;
|
||||
} else {
|
||||
const AVPacket& packet_raw = (packet.data != 0) ? packet : packet_filtered;
|
||||
picture_pts = (packet_raw.pts != AV_NOPTS_VALUE_)
|
||||
? packet_raw.pts
|
||||
: packet_raw.dts;
|
||||
if (frame_number == 0) dts = packet_raw.dts;
|
||||
if (picture_pts < 0) picture_pts = 0;
|
||||
}
|
||||
|
||||
// Decide timestamp offset once on first frame to normalize all timestamps to start at zero.
|
||||
// This handles videos with negative DTS values (e.g., from B-frame reordering) or non-zero
|
||||
// start_time. Similar to FFmpeg's -avoid_negative_ts make_zero option.
|
||||
if (!ts_offset_decided)
|
||||
{
|
||||
int64_t min_start_avtb = INT64_MAX;
|
||||
|
||||
// Check container start_time (already in AV_TIME_BASE units)
|
||||
if (ic && ic->start_time != AV_NOPTS_VALUE_)
|
||||
{
|
||||
min_start_avtb = ic->start_time;
|
||||
}
|
||||
|
||||
// Check stream start_time
|
||||
AVStream* st = ic->streams[video_stream];
|
||||
if (st->start_time != AV_NOPTS_VALUE_)
|
||||
{
|
||||
int64_t s = to_avtb(st->start_time, st->time_base);
|
||||
if (s < min_start_avtb) min_start_avtb = s;
|
||||
}
|
||||
|
||||
// Check first observed timestamp (PTS preferred, else DTS from frame 0)
|
||||
int64_t first_ts_stream = picture_pts;
|
||||
if (first_ts_stream == AV_NOPTS_VALUE_ && dts != AV_NOPTS_VALUE_)
|
||||
{
|
||||
first_ts_stream = dts;
|
||||
}
|
||||
if (first_ts_stream != AV_NOPTS_VALUE_)
|
||||
{
|
||||
int64_t t = to_avtb(first_ts_stream, st->time_base);
|
||||
if (t < min_start_avtb) min_start_avtb = t;
|
||||
}
|
||||
|
||||
// Compute offset to shift negative timestamps to zero
|
||||
ts_offset_avtb = (min_start_avtb != INT64_MAX && min_start_avtb < 0) ? -min_start_avtb : 0;
|
||||
ts_offset_decided = true;
|
||||
}
|
||||
|
||||
// Apply normalization to picture_pts
|
||||
if (picture_pts != AV_NOPTS_VALUE_)
|
||||
{
|
||||
int64_t t = to_avtb(picture_pts, video_st->time_base);
|
||||
t += ts_offset_avtb;
|
||||
picture_pts = from_avtb(t, video_st->time_base);
|
||||
}
|
||||
|
||||
// Also normalize dts
|
||||
if (dts != AV_NOPTS_VALUE_)
|
||||
{
|
||||
int64_t t = to_avtb(dts, video_st->time_base);
|
||||
t += ts_offset_avtb;
|
||||
dts = from_avtb(t, video_st->time_base);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) || LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
|
||||
AVRational frame_rate = video_st->avg_frame_rate;
|
||||
#else
|
||||
@@ -1769,16 +1845,28 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
|
||||
// if hardware frame, copy it to system memory
|
||||
if (picture && picture->hw_frames_ctx) {
|
||||
sw_picture = av_frame_alloc();
|
||||
if (!sw_picture) {
|
||||
CV_LOG_ERROR(NULL, "av_frame_alloc failed");
|
||||
return false;
|
||||
}
|
||||
//if (av_hwframe_map(sw_picture, picture, AV_HWFRAME_MAP_READ) < 0) {
|
||||
if (av_hwframe_transfer_data(sw_picture, picture, 0) < 0) {
|
||||
CV_LOG_ERROR(NULL, "Error copying data from GPU to CPU (av_hwframe_transfer_data)");
|
||||
av_frame_free(&sw_picture);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!sw_picture || !sw_picture->data[0])
|
||||
{
|
||||
#if USE_AV_HW_CODECS
|
||||
if (sw_picture != picture)
|
||||
av_frame_free(&sw_picture);
|
||||
#endif
|
||||
CV_LOG_ERROR(NULL, "Picture does not contain data");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(56, 72, 0)
|
||||
const char* color_space_name = av_color_space_name(sw_picture->colorspace);
|
||||
@@ -1829,7 +1917,14 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
|
||||
|
||||
img_convert_ctx = sws_alloc_context();
|
||||
if (img_convert_ctx == NULL)
|
||||
return false;//CV_Error(0, "Cannot initialize the conversion context!");
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "Cannot initialize the conversion context!");
|
||||
#if USE_AV_HW_CODECS
|
||||
if (sw_picture != picture)
|
||||
av_frame_free(&sw_picture);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
av_opt_set_int(img_convert_ctx, "sws_flags", SWS_BICUBIC, 0);
|
||||
av_opt_set_int(img_convert_ctx, "threads", requestedThreads, 0);
|
||||
@@ -1870,8 +1965,14 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
|
||||
);
|
||||
#endif
|
||||
|
||||
if (img_convert_ctx == NULL)
|
||||
return false;//CV_Error(0, "Cannot initialize the conversion context!");
|
||||
if (img_convert_ctx == NULL) {
|
||||
CV_LOG_ERROR(NULL, "Cannot initialize the conversion context!");
|
||||
#if USE_AV_HW_CODECS
|
||||
if (sw_picture != picture)
|
||||
av_frame_free(&sw_picture);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_AV_FRAME_GET_BUFFER
|
||||
av_frame_unref(&rgb_picture);
|
||||
@@ -1880,7 +1981,11 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
|
||||
rgb_picture.height = buffer_height;
|
||||
if (0 != av_frame_get_buffer(&rgb_picture, 32))
|
||||
{
|
||||
CV_WARN("OutOfMemory");
|
||||
CV_LOG_ERROR(NULL, "Out of memory issue on av_frame_get_buffer!");
|
||||
#if USE_AV_HW_CODECS
|
||||
if (sw_picture != picture)
|
||||
av_frame_free(&sw_picture);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
@@ -2120,8 +2225,13 @@ int64_t CvCapture_FFMPEG::dts_to_frame_number(int64_t dts)
|
||||
|
||||
double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
|
||||
{
|
||||
return (double)(dts - ic->streams[video_stream]->start_time) *
|
||||
r2d(ic->streams[video_stream]->time_base);
|
||||
const AVStream* st = ic->streams[video_stream];
|
||||
int64_t ts = dts;
|
||||
|
||||
if (ts_offset_avtb == 0 && st->start_time != AV_NOPTS_VALUE_)
|
||||
ts -= st->start_time;
|
||||
|
||||
return ts * r2d(st->time_base);
|
||||
}
|
||||
|
||||
void CvCapture_FFMPEG::get_rotation_angle()
|
||||
@@ -2174,9 +2284,19 @@ void CvCapture_FFMPEG::seek(int64_t _frame_number)
|
||||
{
|
||||
int64_t _frame_number_temp = std::max(_frame_number-delta, (int64_t)0);
|
||||
double sec = (double)_frame_number_temp / get_fps();
|
||||
int64_t time_stamp = ic->streams[video_stream]->start_time;
|
||||
double time_base = r2d(ic->streams[video_stream]->time_base);
|
||||
time_stamp += (int64_t)(sec / time_base + 0.5);
|
||||
|
||||
AVStream* st = ic->streams[video_stream];
|
||||
int64_t time_stamp = st->start_time;
|
||||
double time_base = r2d(st->time_base);
|
||||
int64_t ts_norm = (int64_t)(sec / time_base + 0.5);
|
||||
|
||||
if (ts_offset_avtb != 0) {
|
||||
// map normalized target back to original demux timeline
|
||||
time_stamp += ts_norm - from_avtb(ts_offset_avtb, st->time_base);
|
||||
} else {
|
||||
time_stamp += ts_norm;
|
||||
}
|
||||
|
||||
if (get_total_frames() > 1) av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_BACKWARD);
|
||||
if(!rawMode)
|
||||
avcodec_flush_buffers(context);
|
||||
|
||||
@@ -1103,7 +1103,7 @@ bool CvCaptureCAM_V4L::grabFrame()
|
||||
FirstCapture = false;
|
||||
|
||||
#if defined(V4L_ABORT_BADJPEG)
|
||||
// skip first frame. it is often bad -- this is unnotied in traditional apps,
|
||||
// skip first frame. it is often bad -- this is unnoticed in traditional apps,
|
||||
// but could be fatal if bad jpeg is enabled
|
||||
if (!read_frame_v4l2())
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user