Crash when calling CGImageDestinationFinalize with large photo - cocoa-touch

In the code below I am crashing on the CGImageDestinationFinalize() line. The CGImage is a large JPG photo. It works fine if I use a smaller JPG. But anything over 10MB crashes on the device with "out of memory".
I think the CGImageDestination* methods are streaming the write but I must be doing something wrong that is retaining the data.
Is there another API call I should use when writing large JPGs?
BOOL SKImageWriteToDisk(CGImageRef image, NSURL *url, SKImageProfile profile, CGFloat dpi)
{
CFStringRef type = profile == SKmageProfileCompressed ? kUTTypeJPEG : kUTTypePNG;
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)url, type, 1, NULL);
if (!destination) {
return NO;
}
NSMutableDictionary *properties =
[#{
(id) kCGImagePropertyDPIHeight: #(dpi),
(id) kCGImagePropertyDPIWidth: #(dpi)
} mutableCopy];
if (profile == SKImageProfileCompressed) {
properties[(id) kCGImageDestinationLossyCompressionQuality] = #(0.8f);
}
CGImageDestinationAddImage(destination, image, (__bridge CFDictionaryRef)properties);
BOOL finalizeSuccess = CGImageDestinationFinalize(destination); // crash!
CFRelease(destination);
return finalizeSuccess;
}

Related

ZXing can't write png output

I never experienced that issue, I was able to use the same code 3 month ago. After updating to macOS High Sierra, I can't run this code with the ZXing library. Not sure if it's linked but here what I have:
Error:
IIOImageWriteSession:113: cannot create: '/Users/****/Desktop/test.PNG.sb-8ff9679f-SRYKGg'
error = 1 (Operation not permitted)
Code:
NSError * error = nil;
ZXMultiFormatWriter * writer = [ZXMultiFormatWriter writer];
ZXBitMatrix* result = [writer encode:#"AM233X05987"
format:kBarcodeFormatDataMatrix
width:500
height:500
error:&error];
if (result) {
CGImageRef image = [[ZXImage imageWithMatrix:result] cgimage];
CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:#"/Users/****/Desktop/test.PNG"];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
if (!destination) {
NSLog(#"Failed to create CGImageDestination" );
return NO;
}
CGImageDestinationAddImage(destination, image, nil);
if (!CGImageDestinationFinalize(destination)) {
NSLog(#"Failed to write image to /Users/alex/Desktop/Barcodes/");
CFRelease(destination);
return NO;
}
CFRelease(destination);
} else {
NSString * errorMessage = [error localizedDescription];
NSLog(#"%#", errorMessage);
}
Finally found the origin of the problem, it turns out this is because the App is Sandboxed. Turning off Sandbox fixed the issue.
To get this with Sandbox ON, a solution is to put up a Save panel and, if it returns OK, saving the file to the Save panel's URL.

CGImageSourceCreateWithURL returns always NULL

I need to read the properties of an image without load or download it. In fact i have implemented a simple method that use the CGImageSourceCreateWithUrl for accomplish this.
My problem is that it is returning always error because seems that the imageSource is null. So what can i do for fix it?
In the NSURL object i pass urls like:
"http://www.example.com/wp-content/uploads/image.jpg" but also ALAssets library Id used to retrieve images inside the phone like "assets-library://asset/asset.JPG?id=E5F41458-962D-47DD-B5EF-E606E2A8AC7A&ext=JPG".
This is my method:
-(NSString *) getPhotoInfo:(NSString *)paths{
NSString *xmlList = #“test”;
NSURL * imageFileURL = [NSURL fileURLWithPath:paths];
NSLog(#"imageFileURL %#", imageFileURL);
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)(imageFileURL), NULL);
if (imageSource == NULL) {
// Error loading image
NSLog(#"Error loading image");
}
CGFloat width = 0.0f, height = 0.0f;
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
NSLog(#"image source %#", imageSource);
return xmlList;
}
I have saw this posts for try to fix it but nothing seems working:
CGImageSourceRef imageSource = CGImageSourceCreateWithURL returns NULL
CGImageSourceCreateWithURL with authentication
accessing UIImage properties without loading in memory the image
In my project ARC is enabled.
Thanks
If you are passing the string "http://www.example.com/wp-content/uploads/image.jpg” to -fileURLWithPath: it’s going to return nil, because that string is sure not a file path, it’s a URL string.
Think of -fileURLWithPath: as just prepending the string you pass in with “file://localhost/“...so you’d end up with a URL that looks like "file://localhost/http://www.example.com/wp-content/uploads/image.jpg”. That’s not good.
You need to call [NSURL URLWithString:paths] if you’re going to be passing in entire URL strings, not just a filesystem path strings.
Working with "assets-library://asset/asset.JPG?id.........., try this code
-(UIImage*)resizeImageToMaxSize:(CGFloat)max anImage:(UIImage*)anImage
{
NSData * imgData = UIImageJPEGRepresentation(anImage, 1);
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);
if (!imageSource)
return nil;
CFDictionaryRef options = (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
(id)[NSNumber numberWithFloat:max], (id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef imgRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options);
UIImage* scaled = [UIImage imageWithCGImage:imgRef];
//these lines might be skipped for ARC enabled
CGImageRelease(imgRef);
CFRelease(imageSource);
return scaled; }

Adding audio to a video using Obj-C plugin and AVAssetWriterInput

I'm trying to take a video created using the iVidCap plugin and add audio to it. Basically the exact same thing as in this question: Writing video + generated audio to AVAssetWriterInput, audio stuttering. I've used the code from this post as a basis to try and modify the iVidCap.mm file myself, but the app always crashes in endRecordingSession.
I'm not sure how I need to modify endRecordingSession to accomodate for the audio (the original plugin just creates a video file). Here is the function:
- (int) endRecordingSession: (VideoDisposition) action {
NSLog(#"Start endRecordingSession");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Auto released pool");
NSString *filePath;
BOOL success = false;
[videoWriterInput markAsFinished];
NSLog(#"Mark video writer input as finished");
//[audioWriterInput markAsFinished];
// Wait for the video status to become known.
// Is this really doing anything?
int status = videoWriter.status;
while (status == AVAssetWriterStatusUnknown) {
NSLog(#"Waiting for video to complete...");
[NSThread sleepForTimeInterval:0.5f];
status = videoWriter.status;
}
NSLog(#"Video completed");
#synchronized(self) {
success = [videoWriter finishWriting];
NSLog(#"Success: %#", success);
if (!success) {
// We failed to successfully finalize the video file.
NSLog(#"finishWriting returned NO");
} else {
// The video file was successfully written to the Documents folder.
filePath = [[self getDocumentsFileURL:videoFileName] path];
if (action == Save_Video_To_Album) {
// Move the video to an accessible location on the device.
NSLog(#"Temporary video filePath=%#", filePath);
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(filePath)) {
NSLog(#"Video IS compatible. Adding it to photo album.");
UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, #selector(copyToPhotoAlbumCompleteFromVideo: didFinishSavingWithError: contextInfo:), nil);
} else {
NSLog(#"Video IS NOT compatible. Could not be added to the photo album.");
success = NO;
}
} else if (action == Discard_Video) {
NSLog(#"Video cancelled. Removing temporary video file: %#", filePath);
[self removeFile:filePath];
}
}
[self cleanupWriter];
}
isRecording = false;
[pool drain];
return success; }
Right now it crashes on [videoWriter finishWriting]. I tried adding [audioWriterInput markAsFinished], but then it crashes on that. I would contact the original poster since it seems like they got it working, but there doesn't seem to be a way to send private messages.
Does anyone have any suggestions on how I can get this to work or why it's crashing? I've tried my best to figure this out but I'm pretty new to Obj-C. I can post the rest of the code if needed (a lot of it is in the original post referenced earlier).
The issue might actually be in the writeAudioBuffer function.
If you copied the code from that post but didnlt change it then you will certainly have some problems.
You need to do something like this:
if ( ![self waitForAudioWriterReadiness]) {
NSLog(#"WARNING: writeAudioBuffer dropped frame after wait limit reached.");
return 0;
}
OSStatus status;
CMBlockBufferRef bbuf = NULL;
CMSampleBufferRef sbuf = NULL;
size_t buflen = n * nchans * sizeof(float);
CMBlockBufferRef tmp_bbuf = NULL;
status = CMBlockBufferCreateWithMemoryBlock(
kCFAllocatorDefault,
samples,
buflen,
kCFAllocatorDefault,
NULL,
0,
buflen,
0,
&tmp_bbuf);
if (status != noErr || !tmp_bbuf) {
NSLog(#"CMBlockBufferCreateWithMemoryBlock error");
return -1;
}
// Copy the buffer so that we get a copy of the samples in memory.
// CMBlockBufferCreateWithMemoryBlock does not actually copy the data!
//
status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, tmp_bbuf, kCFAllocatorDefault, NULL, 0, buflen, kCMBlockBufferAlwaysCopyDataFlag, &bbuf);
//CFRelease(tmp_bbuf); // causes abort?!
if (status != noErr) {
NSLog(#"CMBlockBufferCreateContiguous error");
//CFRelease(bbuf);
return -1;
}
CMTime timestamp = CMTimeMake(sample_position_, 44100);
status = CMAudioSampleBufferCreateWithPacketDescriptions(
kCFAllocatorDefault, bbuf, TRUE, 0, NULL, audio_fmt_desc_, 1, timestamp, NULL, &sbuf);
sample_position_ += n;
if (status != noErr) {
NSLog(#"CMSampleBufferCreate error");
return -1;
}
BOOL r = [audioWriterInput appendSampleBuffer:sbuf];
if (!r) {
NSLog(#"appendSampleBuffer error");
}
//CFRelease(bbuf); // crashes, don't know why.. Is there a leak here?
//CFRelease(sbuf);
return 0;
There are a few things to do with memory management that I am unsure on here.
Additionally be sure to use:
audioWriterInput.expectsMediaDataInRealTime = YES;

Get a list of unmountable drives using Cocoa

I would like to obtain a list of drives that are unmountable/ejectable using Cocoa/Objective-C under OS X.
I was hoping that NSWorkspace getFileSystemInfoForPath::::: would help me:
NSArray* listOfMedia = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths];
NSLog(#"%#", listOfMedia);
for (NSString* volumePath in listOfMedia)
{
BOOL isRemovable = NO;
BOOL isWritable = NO;
BOOL isUnmountable = NO;
NSString* description = [NSString string];
NSString* type = [NSString string];
BOOL result = [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:volumePath
isRemovable:&isRemovable
isWritable:&isWritable
isUnmountable:&isUnmountable
description:&description
type:&type];
NSLog(#"Result:%i Volume: %#, Removable:%i, W:%i, Unmountable:%i, Desc:%#, type:%#", result, volumePath, isRemovable, isWritable, isUnmountable, description, type);
}
Output:
...
Result:1 Volume: /Volumes/LR Photos, Removable:0, W:1, Unmountable:0, Desc:hfs, type:hfs
...
"LR Photos" is an external drive (connected via Thunderbolt) that should be removable and/or unmountable (or, at least I think it should be). :)
Should I be going about this a different way?
Thanks in advance!
You can use diskArbitration framework.
#import <DiskArbitration/DiskArbitration.h>
+(NSMutableArray *)getListOfEjectableMedia
{
NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
NSMutableArray *result = [NSMutableArray array];
for(NSURL *volURL in mountedRemovableMedia)
{
int err = 0;
DADiskRef disk;
DASessionRef session;
CFDictionaryRef descDict;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
if (session == NULL) {
err = EINVAL;
}
}
if (err == 0) {
descDict = DADiskCopyDescription(disk);
if (descDict == NULL) {
err = EINVAL;
}
}
if (err == 0) {
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
if (mediaEjectableKey != NULL)
{
BOOL op = CFEqual(mediaEjectableKey, CFSTR("0")) || CFEqual(deviceProtocolName, CFSTR("USB"));
if (op) {
[result addObject:volURL];
}
}
}
if (descDict != NULL) {
CFRelease(descDict);
}
if (disk != NULL) {
CFRelease(disk);
}
if (session != NULL) {
CFRelease(session);
}
}
return result;
}
Unfortunately getFileSystemInfoForPath: is not really the right way to do this. What removable means is that the volume is on removable media such as a CD or DVD. In practice unmountable seems to give the same results as removable. See for example, this post on results using getFileSystemInfoForPath. So unless you want to simply know if a volume is on removable media, you'll need to use another technique.
What you really want to check is the connection bus type of the volume. Firewire, USB, Thunderbolt, etc. are unmountable in the sense you mean. You can see this information in Disk Utility if you select the volume and push the "Info" button under "Connection Bus". Getting this information programmatically is much harder and as far as I can tell is only possible using the IOKit. Details are in Apple's documentation on Accessing Hardware from Applications.
you can use command line version of Disk Utility app that is "diskutil", run it with parameter "list" and pipe output and get it in your program ( don't need to use cocoa ).

Recommended way to copy arbitrary files using Cocoa

I need to copy file from one OS X volume to another OS X volume. While an *.app isn't strictly speaking a file but a folder, user expect them to be a unit. Thus, if user selects a file, the app should not show its folder's contents, but copy it as a unit.
Therefore I ask, if there exists a recommended way to copy files using pure Cocoa code.
Optional: Which command line tool provides help and could be utilized by a Cocoa application.
NSFileManager is your friend:
NSError *error = nil;
if ([[NSFileManager defaultManager] copyItemAtPath:#"path/to/source" toPath:#"path/to/destination" error:&error])
{
// copy succeeded
}
else
{
// copy failed, print error
}
You can also use FSCopyObjectAsync function. You can display file copy progress and you can also cancel file copy using FSCopyObjectAsync().
Take a look at FSFileOperation example code.
This sample shows how to copy and move both files and folders. It
shows both the synchronous and asynchronous (using CFRunLoop) use of
the FSFileOperation APIs. In addition, it shows path and FSRef
variants of the API and how to get status out of the callbacks. The
API is conceptually similar to the FSVolumeOperation APIs introduced
in Mac OS X 10.2.
Example of FSCopyObjectAsync:
#import <Cocoa/Cocoa.h>
#interface AsyncCopyController : NSObject {
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object;
//delegate method
-(void)didReceiveCurrentPath : (NSString *)curremtItemPath bytesCompleted : (unsigned long long)floatBytesCompleted currentStageOfFileOperation : (unsigned long)stage;
-(void)didCopyOperationComplete : (BOOL)boolean;
-(void)didReceiveCopyError : (NSString *)Error;
-(void)cancelAllAsyncCopyOperation;
#end
#import "AsyncCopyController.h"
static Boolean copying= YES;
#implementation AsyncCopyController
static void statusCallback (FSFileOperationRef fileOp,
const FSRef *currentItem,
FSFileOperationStage stage,
OSStatus error,
CFDictionaryRef statusDictionary,
void *info )
{
NSLog(#"Callback got called. %ld", error);
id delegate;
if (info)
delegate = (id)info;
if (error!=0) {
if (error==-48) {
[delegate didReceiveCopyError:#"Duplicate filename and version or Destination file already exists or File found instead of folder"];
}
}
CFURLRef theURL = CFURLCreateFromFSRef( kCFAllocatorDefault, currentItem );
NSString* currentPath = [(NSURL *)theURL path];
// NSLog(#"currentPath %#", currentPath);
// If the status dictionary is valid, we can grab the current values to
// display status changes, or in our case to update the progress indicator.
if (statusDictionary)
{
CFNumberRef bytesCompleted;
bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
kFSOperationBytesCompleteKey);
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberMaxType,
&floatBytesCompleted);
// NSLog(#"Copied %d bytes so far.",
// (unsigned long long)floatBytesCompleted);
if (info)
[delegate didReceiveCurrentPath :currentPath bytesCompleted :floatBytesCompleted currentStageOfFileOperation:stage];
}
NSLog(#"stage %d", stage);
if (stage == kFSOperationStageComplete) {
NSLog(#"Finished copying the file");
if (info)
[delegate didCopyOperationComplete:YES];
// Would like to call a Cocoa Method here...
}
if (!copying) {
FSFileOperationCancel(fileOp);
}
}
-(void)cancelAllAsyncCopyOperation
{
copying = NO;
}
-(OSStatus)copySource : (NSString *)aSource ToDestination: (NSString *)aDestDir setDelegate : (id)object
{
NSLog(#"copySource");
copying = YES;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
NSLog(#"%#", runLoop);
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);
require(fileOp, FSFileOperationCreateFailed);
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp,
runLoop, kCFRunLoopDefaultMode);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
return status;
}
require_noerr(status, FSFileOperationScheduleWithRunLoopFailed);
if (status) {
NSLog(#"Failed to schedule operation with run loop: %#", status);
//return NO;
}
// Create a filesystem ref structure for the source and destination and
// populate them with their respective paths from our NSTextFields.
FSRef source;
FSRef destination;
// Used FSPathMakeRefWithOptions instead of FSPathMakeRef
// because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference
status = FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&source,
NULL);
require_noerr(status, FSPathMakeRefWithOptionsaSourceFailed);
Boolean isDir = true;
status = FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
kFSPathMakeRefDefaultOptions,
&destination,
&isDir);
require_noerr(status, FSPathMakeRefWithOptionsaDestDirFailed);
// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)
FSFileOperationClientContext clientContext;
// The FSFileOperation will copy the data from the passed in clientContext so using
// a stack based record that goes out of scope during the operation is fine.
if (object)
{
clientContext.version = 0;
clientContext.info = (void *) object;
clientContext.retain = CFRetain;
clientContext.release = CFRelease;
clientContext.copyDescription = CFCopyDescription;
}
// Start the async copy.
status = FSCopyObjectAsync (fileOp,
&source,
&destination, // Full path to destination dir
NULL,// Use the same filename as source
kFSFileOperationDefaultOptions,
statusCallback,
1.0,
object != NULL ? &clientContext : NULL);
//CFRelease(fileOp);
NSLog(#"Failed to begin asynchronous object copy: %d", status);
if (status) {
NSString * errMsg = [NSString stringWithFormat:#" - %#", status];
NSLog(#"Failed to begin asynchronous object copy: %d", status);
}
if (object)
{
[object release];
}
FSFileOperationScheduleWithRunLoopFailed:
CFRelease(fileOp);
FSPathMakeRefWithOptionsaSourceFailed:
FSPathMakeRefWithOptionsaDestDirFailed:
FSFileOperationCreateFailed:
return status;
}
#end
FSCopyObjectAsync is Deprecated in OS X v10.8
copyfile(3) is alternative for FSCopyObjectAsync. Here is example of copyfile(3) with Progress Callback.