How to get bytes from NSPasteboard - objective-c

there's a binary file opened in Hex Fiend:
Hex Fiend Snapshot
I highlighted the first 288 bytes and copied with CMD+C
then I tried to retrieve it from code:
NSPasteboard *pboard = NSPasteboard.generalPasteboard;
NSString *content = [pboard stringForType:NSPasteboardTypeString];
NSData *data1 = [content dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
NSData *data2 = [pboard dataForType:NSPasteboardTypeString];
data1 and data2 look something like:
{length = 435, bytes = 0x41c39a67 c28d3c18 c3847a1e c2bf5dc2 ... bac3884f 3274c3b8 }
I pasted the content with CMD+V into Sublime Text and VS code, they can both show it right:
Sublime Snapshot
My question is: how to get the exact 288 bytes back from NSPasteboard
Thank you guys!
Update:
NSPasteboard *pboard = NSPasteboard.generalPasteboard;
for (NSString *t in pboard.types) {
NSLog(#"pboard type: %#, data length: %ld", t, [pboard dataForType:t].length);
}
NSLog(#"----");
for (NSPasteboardItem *item in [pboard pasteboardItems]) {
for (NSString *type in [item types]) {
NSData *data = [item dataForType:type];
NSLog(#"item type: %#, data length: %ld", type, data.length);
}
}
result:
2022-04-11 16:08:59.726507+0800 testapp[50006:2713553] pboard type: dyn.ah62d4rv4gu8yuvwusmy1q2pyqzbhw7dfkf3he2p3nbvg82pwqvnhw6df, data length: 341
2022-04-11 16:08:59.726649+0800 testapp[50006:2713553] pboard type: HFPrivateByteArrayPboardType, data length: 341
2022-04-11 16:08:59.733329+0800 testapp[50006:2713553] pboard type: public.utf8-plain-text, data length: 435
2022-04-11 16:08:59.733525+0800 testapp[50006:2713553] pboard type: NSStringPboardType, data length: 435
2022-04-11 16:08:59.733581+0800 testapp[50006:2713553] ----
2022-04-11 16:08:59.733722+0800 testapp[50006:2713553] item type: dyn.ah62d4rv4gu8yuvwusmy1q2pyqzbhw7dfkf3he2p3nbvg82pwqvnhw6df, data length: 341
2022-04-11 16:08:59.733806+0800 testapp[50006:2713553] item type: public.utf8-plain-text, data length: 435
I still cannot get the expected 288 bytes
Update2
Snapshot from Clipboard Viewer shows bytes for invisible characters are modified.
HFPrivateByteArrayPboardType snapshot
invisible characters snapshot
Yes, HexFiend is open sourced but the code is complicated for me :(
I have not yet figured out how to read from HFPrivateByteArrayPboardType plist.

Sorry guys, I was wrong.
After my double check, neither Sublime Text nor VS Code, can correctly get back the origin 288 bytes. They can only show 288 converted characters actually (435 bytes). My bad!
It seems like it is not possible to safely pass binary data across different apps via general string type clipboard. (Please correct me if I'm wrong :) )

Related

JSON file read 3840 error

Reading this file city.list.us.json.gz (uncompressed) gets an error (3840) doing:
NSString * path = [mb pathForResource:#"city.list.us" ofType:#"json" inDirectory:#"JSON"];
NSString * string = [NSString stringWithContentsOfUTF8File:path];
NSData * data = [string dataUsingEncoding:NSASCIIStringEncoding];
NSLog(#"isValidJSONObject:%#", [NSJSONSerialization isValidJSONObject:data] ? #"Yes" : #"No");
NSError * bobo = nil;
id blob = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&bobo];
[NSApp presentError:bobo];
which reads ok - length 1873878 bytes, looks ok in vi (set list), but does not yield a non-nil object.
A head -4 of the file shows:
{"_id":4070245,"name":"Jones Crossroads","country":"US","coord":{"lon":-85.484657,"lat":31.21073}}
{"_id":4344544,"name":"Vernon Parish","country":"US","coord":{"lon":-93.183502,"lat":31.11685}}
{"_id":4215307,"name":"Pennick","country":"US","coord":{"lon":-81.55899,"lat":31.313}}
{"_id":5285039,"name":"Black Bear Spring","country":"US","coord":{"lon":-110.288139,"lat":31.386209}}
To my un-expert JSON eyes, it appears this is a file of cites, has an object per line with 4 values (_id,name,country,coord), last an object containing 2 values (lat,lon).
Also tried NSASCIIStringEncoding for the NSData conversion but no joy.
Any ideas?
That's not valid JSON. It's a large number of JSON dictionaries but they are not related to each other.
Most likely the expected JSON (an array of dictionaries) is supposed to look like
[{"_id":4070245,"name":"Jones Crossroads","country":"US","coord":{"lon":-85.484657,"lat":31.21073}},
{"_id":4344544,"name":"Vernon Parish","country":"US","coord":{"lon":-93.183502,"lat":31.11685}},
{"_id":4215307,"name":"Pennick","country":"US","coord":{"lon":-81.55899,"lat":31.313}},
{"_id":5285039,"name":"Black Bear Spring","country":"US","coord":{"lon":-110.288139,"lat":31.386209}}]
I wrapped the whole text in square brackets [] and added commas at the end of each line.

How do I extract the width and height of a PNG from looking at the header in objective c?

I have a need to find the dimensions of images online without downloading them. To accomplish this I do this:
+ (CGSize) getImageDimensions:(NSString *)url {
// Send a synchronous request
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: url]];
NSString *rangeString = [url hasSuffix: #"png"] ? #"bytes=0-100" : #"bytes=0-1300";
[urlRequest setValue:rangeString forHTTPHeaderField:#"Range"];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error == nil)
return [UIImage imageWithData: data].size;
else
return CGSizeMake(0, 0);
}
This (downloading the first 100 bytes) surprisingly works and I get the correct dimensions for PNGs this way.
However I do not think this is a very elegant approach. First, I chose to download the first 100 bytes by just guess and checking, making it as small as possible whilst still having it work okay.
Apparently in a PNG file there's this thing called a IHDR in the header and I have to find it and directly after it are the width and height. This gives me the impression that I should loop over the data and find this IHDR and get the dimensions. The problem is, when I NSLog the data I get something like this:
... 49484452 000003b7 000001a7 08060000 006c2da0 b100000a 41694343 50494343 2050726f 66696c65 ...
I have no idea how to handle looping over my NSData object and detecting the IHDR token and then converting the things that come after it to numbers. I also have no idea if requesting just 100 bytes for a PNG is requesting too much just to get the dimensions, or if it's requesting not enough
Rationale
According to the PNG specification:
The first eight bytes of a PNG file always contain the following
(decimal) values: 137 80 78 71 13 10 26 10
So you have to read those to make sure you really have a PNG file.
Then,
The IHDR chunk must appear FIRST. It contains:
Width: 4 bytes
Height: 4 bytes
etc...
So according to the structure of chunks, you first have to read the four bytes representing the length of the chunk's data field, then the four bytes representing the chunk's name, then the two 32-bit integers representing width and height, eight bytes in total.
So, the exact minimal number of bytes you must read in order to determine width and height is 8 + 4 + 4 + 8 = 24 bytes.
Objective-C code
Once you have your NSData object, you simply have to access the bytes that it contains:
unsigned char buffer[24];
[data getBytes:buffer length:24];
Optionally, but I recommend it strongly, check that you indeed have a PNG file:
unsigned char png_header[] = {137, 80, 78, 71, 13, 10, 26, 10};
if (memcmp(buffer, png_header, 8)) {
// this is not a PNG !
}
Make sure you have an IHDR:
unsigned char ihdr_name[] = "IHDR";
if (memcmp(buffer+8+4, ihdr_name, 4)) {
// not an IHDR chunk, invalid PNG file
}
Width and height can be accessed as big-endian encoded unsigned ints at offset 24 minus 8 and minus 4 bytes respectively:
unsigned int width = OSReadBigInt32(buffer + 24 - 8);
unsigned int height = OSReadBigInt32(buffer + 24 - 4);
Edit: Fixed buffer reading code: PNG actually stores integers in big-endian.

autosavesInPlace causes New Document save to fail

I have a NSPersistentDocument based app which fails to save a new document when autosavesInPlace is set to return YES , return NO and the problem disappears.
I create a new document
Make some changes
Save it , thus running NSSaveAsOperation , the name of the document and the URL changes and all appears to be well but the next save will throw a very descriptive
NSPersistentStoreSaveError = 134030, // unclassified save error - something we depend on returned an error
This only happens when the document attempts to run a save after a NSSaveAsOperation. Any other save type will work fine as in changes to existing doc. Interesting effect is that if i dont change name or location i dont get this issue either.
Im getting an exception backtrace of
frame #0: 0x00007fff988143c5 libobjc.A.dylibobjc_exception_throw
frame #1: 0x00007fff94c5f5f9 CoreData-[NSPersistentStore(_NSInternalMethods) _preflightCrossCheck] + 697
frame #2: 0x00007fff94c3198b CoreData-[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 603
frame #3: 0x00007fff94c5aa98 CoreData-[NSManagedObjectContext save:] + 456
frame #4: 0x00007fff91baa101 AppKit-[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 3743
frame #5: 0x0000000100002de7 ZZZZ-[ZZZZDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] + 135 at ZZZZDocument.m:209
frame #6: 0x00007fff91baabc7 AppKit-[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 611
frame #7: 0x0000000100002ea3 ZZZZ-[ZZZZDocument writeSafelyToURL:ofType:forSaveOperation:error:] + 115 at ZZZZDocument.m:223
any ideas?
Its not possible for a un-wrappered core data file
In the event you attempt to trap NSSaveAsOperation and do a migrate on the persistent store for that enum the construction of the ...-journal file will fail to create as its outside the sandbox.
-(void)saveToURL:(NSURL *)url ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation completionHandler:(void (^)(NSError *))completionHandler
{
NSLog(#" save op = %ld to %# ",saveOperation,url);
NSURL *targeturl = url;
if(saveOperation == NSSaveAsOperation)
{
//migrate pstore
NSPersistentStore *store = [self.managedObjectContext.persistentStoreCoordinator.persistentStores lastObject];
if (store)
{
NSMutableDictionary *opts = [NSMutableDictionary dictionary];
[opts setValue:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
[opts setValue:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
NSError *error = NULL;
NSPersistentStore *newstore = [self.managedObjectContext.persistentStoreCoordinator migratePersistentStore:store toURL:url options:opts withType:store.type error:&error];
if(newstore == nil)
{
NSLog(#"migration error %#",[error localizedDescription]);
}
self.fileURL = url;
}
}
[super saveToURL:targeturl ofType:typeName forSaveOperation:saveOperation completionHandler:completionHandler];
}
So we need to wrapper the file in a bundle/folder which is non-trivial using the NSPersistentDocument framework.
Heres waiting for NSManagedDocument (thats a wishing well API)

Data corruption when reading realtime H.264 output from AVAssetWriter

I'm using some tricks to try to read the raw output of an AVAssetWriter while it is being written to disk. When I reassemble the individual files by concatenating them, the resulting file is the same exact number of bytes as the AVAssetWriter's output file. However, the reassembled file will not play in QuickTime or be parsed by FFmpeg because there is data corruption. A few bytes here and there have been changed, rendering the resulting file unusable. I assume this is occurring on the EOF boundary of each read, but it isn't consistent corruption.
I plan to eventually use code similar to this to parse out individual H.264 NAL units from the encoder to packetize them and send them over RTP, however if I can't trust the data being read from disk I might have to use another solution.
Is there an explanation/fix for this data corruption? And are there any other resources/links you have found on how to parse the NAL units to packetize over RTP?
Full code here: AVAppleEncoder.m
// Modified from
// http://www.davidhamrick.com/2011/10/13/Monitoring-Files-With-GCD-Being-Edited-With-A-Text-Editor.html
- (void)watchOutputFileHandle
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
int fildes = open([[movieURL path] UTF8String], O_EVTONLY);
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,fildes,
DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
queue);
dispatch_source_set_event_handler(source, ^
{
unsigned long flags = dispatch_source_get_data(source);
if(flags & DISPATCH_VNODE_DELETE)
{
dispatch_source_cancel(source);
//[blockSelf watchStyleSheet:path];
}
if(flags & DISPATCH_VNODE_EXTEND)
{
//NSLog(#"File size changed");
NSError *error = nil;
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:movieURL error:&error];
if (error) {
[self showError:error];
}
[fileHandle seekToFileOffset:fileOffset];
NSData *newData = [fileHandle readDataToEndOfFile];
if ([newData length] > 0) {
NSLog(#"newData (%lld): %d bytes", fileOffset, [newData length]);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *movieName = [NSString stringWithFormat:#"%d.%lld.%d.mp4", fileNumber, fileOffset, [newData length]];
NSString *path = [NSString stringWithFormat:#"%#/%#", basePath, movieName];
[newData writeToFile:path atomically:NO];
fileNumber++;
fileOffset = [fileHandle offsetInFile];
}
}
});
dispatch_source_set_cancel_handler(source, ^(void)
{
close(fildes);
});
dispatch_resume(source);
}
Here are some similar questions I have found, but don't exactly answer my question:
Get PTS from raw H264 mdat generated by iOS AVAssetWriter
streaming video FROM an iPhone
Parsing h.264 NAL units from a quicktime MOV file
Realtime Audio/Video Streaming FROM iPhone to another device (Browser, or iPhone)
When I eventually figure this out, I will release an open source library to assist people who try to do this in the future.
Thank you!
Update: The corruption doesn't happen at the EOF boundary. It seems like parts of the file are re-written after finishWriting is called. This first file was chunked at 4KB, so the area changed isn't anywhere near an EOF boundary. It seems to be corrupted near new "moov" elements as well when movieFragmentInterval is enabled.
Correct file on the left, broken file on the right.
I ended up abandoning the "read while it's written" approach in favor of a manual chunking approach where I call finishWriting every 5 seconds on a background thread. I was able to drop a negligible number of frames using a method originally described here:
- (void) segmentRecording:(NSTimer*)timer {
AVAssetWriter *tempAssetWriter = self.assetWriter;
AVAssetWriterInput *tempAudioEncoder = self.audioEncoder;
AVAssetWriterInput *tempVideoEncoder = self.videoEncoder;
self.assetWriter = queuedAssetWriter;
self.audioEncoder = queuedAudioEncoder;
self.videoEncoder = queuedVideoEncoder;
//NSLog(#"Switching encoders");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[tempAudioEncoder markAsFinished];
[tempVideoEncoder markAsFinished];
if (tempAssetWriter.status == AVAssetWriterStatusWriting) {
if(![tempAssetWriter finishWriting]) {
[self showError:[tempAssetWriter error]];
}
}
if (self.readyToRecordAudio && self.readyToRecordVideo) {
NSError *error = nil;
self.queuedAssetWriter = [[AVAssetWriter alloc] initWithURL:[self newMovieURL] fileType:(NSString *)kUTTypeMPEG4 error:&error];
if (error) {
[self showError:error];
}
self.queuedVideoEncoder = [self setupVideoEncoderWithAssetWriter:self.queuedAssetWriter formatDescription:videoFormatDescription bitsPerSecond:videoBPS];
self.queuedAudioEncoder = [self setupAudioEncoderWithAssetWriter:self.queuedAssetWriter formatDescription:audioFormatDescription bitsPerSecond:audioBPS];
//NSLog(#"Encoder switch finished");
}
});
}
Full source code: https://github.com/chrisballinger/FFmpeg-iOS-Encoder/blob/master/AVSegmentingAppleEncoder.m
When reading a MOV file that is ACTIVELY recording on iOS, you MUST check the 4 bytes mentioned for changes, and re-write this four bytes, then check for additional data in file, and send additional data. Then when done, truncate the file to the file size written.
Obviously this depends on where you are sending the file. I use a send (offset,number of bytes) to receiver. So I send "additional data", "more additional data", ... , new data at (24,4), "more additional data".
Typically iOS only writes the 4 byte (size of data section) record when file is about to be closed (aka after last media write). (see info on "Quicktime atoms"). Unfortunately, this also means the MOV file is not PLAYABLE until recording is completed (and movie descriptors written at END of file).

objective-c file size formatter

Is there build in methods for formatting file sizes in Objective C? Or may be you could suggest me some library/source code/etc.?
What I mean is you have some file size that should be displayed something like this depending on given size:
1234 kb
1,2 mb
etc..
Thanks in advance
This one solves the problem quite elegantly:
[NSByteCountFormatter stringFromByteCount:countStyle:]
Example usage:
long long fileSize = 14378165;
NSString *displayFileSize = [NSByteCountFormatter stringFromByteCount:fileSize
countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"Display file size: %#", displayFileSize);
fileSize = 50291;
displayFileSize = [NSByteCountFormatter stringFromByteCount:fileSize
countStyle:NSByteCountFormatterCountStyleFile];
NSLog(#"Display file size: %#", displayFileSize);
Log output:
Display file size: 14.4 MB
Display file size: 50 KB
The output will be formatted properly according to the device's regional settings.
Available since iOS 6.0 and OS X 10.8.
Here's some code I found lying around. Not terribly efficient, and probably better attached to a category of NSNumberFormatter than NSNumber, and so on, but it seems to work
#interface NSNumber (FormatKibi)
- (NSString *)formatKibi;
#end
#implementation NSNumber (FormatKibi)
- (NSString *)formatKibi {
double value = [self doubleValue];
static const char suffixes[] = { 0, 'k', 'm', 'g', 't' };
int suffix = 0;
if (value <= 10000)
return [[NSString stringWithFormat:#"%5f", value]
substringToIndex:5];
while (value > 9999) {
value /= 1024.0;
++suffix;
if (suffix >= sizeof(suffixes)) return #"!!!!!";
}
return [[[NSString stringWithFormat:#"%4f", value]
substringToIndex:4]
stringByAppendingFormat:#"%c", suffixes[suffix]];
}
#end
I tested it with this:
int main(int argc, char *argv[]) {
for (int i = 1; i != argc; ++i) {
NSNumber *n = [NSNumber numberWithInteger:
[[NSString stringWithUTF8String:argv[i]]
integerValue]];
printf("%s ", [[n formatKibi] UTF8String]);
}
printf("\n");
return 0;
}
Then:
$ ./sizeformat 1 12 123 1234 12345 123456 1234567 12345678 1234567890 123456789012 12345678901234 1234567890123456 123456789012345678
1.000 12.00 123.0 1234. 12.0k 120.k 1205k 11.7m 1177m 114.g 11.2t 1122t !!!!!
Get the file size and then calculate whether it is in bytes or kb or mb
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:URL error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
long long fileSize = [fileSizeNumber longLongValue];
Then conversion table
1 byte = 8 bits
1 KiB = 1,024 bytes
1 MiB = 1024 kb
1 GiB = 1024 mb
check this link