Reading samples via AVAssetReader - objective-c

How do you read samples via AVAssetReader? I've found examples of duplicating or mixing using AVAssetReader, but those loops are always controlled by the AVAssetWriter loop. Is it possible just to create an AVAssetReader and read through it, getting each sample?
Thanks.

It's unclear from your question whether you are talking about video samples or audio samples. To read video samples, you need to do the following:
Construct an AVAssetReader:
asset_reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
(error checking goes here)
Get the video track(s) from your asset:
NSArray* video_tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack* video_track = [video_tracks objectAtIndex:0];
Set the desired video frame format:
NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:[NSNumber numberWithInt:<format code from CVPixelBuffer.h>] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
Note that certain video formats just will not work, and if you're doing something real-time, certain video formats perform better than others (BGRA is faster than ARGB, for instance).
Construct the actual track output and add it to the asset reader:
AVAssetReaderTrackOutput* asset_reader_output = [[AVAssetReaderTrackOutput alloc] initWithTrack:video_track outputSettings:dictionary];
[asset_reader addOutput:asset_reader_output];
Kick off the asset reader:
[asset_reader startReading];
Read off the samples:
CMSampleBufferRef buffer;
while ( [asset_reader status]==AVAssetReaderStatusReading )
buffer = [asset_reader_output copyNextSampleBuffer];

damian's answer worked for me with video, and one minor modification: In step 3, I think the dictionary needs to be mutable:
NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init];

Related

ITLibrary gives me nothing but (null)

My program automates a radio station. There is lots of communication back and forth between it and iTunes. I programmed it with scripting bridge. Scripting bridge suffers from memory leaks. Each call to scripting bridge leaks a small amount of memory. Add a lot of calls to a program that runs 24/7 and I've got software that will run for something less than 24 hours, and then quit.
My first attempt at a solution was to minimize my calls to scripting bridge. In researching that end, I came across ItunesLibrary. It isn't working for me.
NSError *error = nil;
ITLibrary *library = [ITLibrary libraryWithAPIVersion:#"1.0" error:&error];
if (library)
{
NSArray *playlists = [[NSArray alloc]init];
playlists = library.allPlaylists;
NSArray *tracks = [[NSArray alloc]init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"mediaKind == %d", ITLibMediaItemMediaKindSong];
tracks = [library.allMediaItems filteredArrayUsingPredicate:predicate];
NSLog(#"Playlists - %#",playlists);
NSLog(#"Tracks - %#",tracks);
}
This code is pretty much right out of Apple's docs. It should work - I think.
Before I added the predicate, I got some info on each of the podcasts in my iTunes library. In the nslog output, each of my playlists produces an entry similar to "". Each of my songs shows nothing more than (null).
All of the info is in iTunes. I can read it with scripting bridge. I can read it with AVAsset
AVAsset *asset = [AVURLAsset URLAssetWithURL:myUrl options:nil];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem* item in metadata )
{
NSString *key = [item commonKey];
NSString *value = [item stringValue];
NSLog(#"key = %#, value = %#", key, value);
}
With AVAsset I only get the song name, album name, and artist name. I need to access the rest of iTune's ID3 tags.
What have I don to break ItunesLibrary?
The secret to getting ItunesLibrary to work seems to be in the entitlements file. You need to add the key "com.apple.security.assets.music.read-only", and set it to YES. I got this by digging through a project on github.com.

AVCaptureSession resolution doesn't change with AVCaptureSessionPreset

I want to change the resolution of pictures I take with the camera on OS X with AV Foundation.
But even if I change the resolution of my AVCaptureSession, the output picture size doesn't change. I always have a 1280x720 picture.
I want a lower resolution because I use these pictures in a real time process and I want the program to be faster.
This is a sample of my code:
session = [[AVCaptureSession alloc] init];
if([session canSetSessionPreset:AVCaptureSessionPreset640x360]) {
[session setSessionPreset:AVCaptureSessionPreset640x360];
}
AVCaptureDeviceInput *device_input = [[AVCaptureDeviceInput alloc] initWithDevice:
[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][0] error:nil];
if([session canAddInput:device_input])
[session addInput:device_input];
still_image = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *output_settings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
[still_image setOutputSettings : output_settings];
[session addOutput:still_image];
What should I change in my code?
I have also run into this issue and found a solution that seems to work. For some reason on OS X, StillImageOutput breaks the capture session presets.
What I did was change the AVCaptureDevice's active format directly. Try this code right after you add your StillImageOutput to your Capture Session.
//Get a list of supported formats for the device
NSArray *supportedFormats = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][0] formats];
//Find the format closest to what you are looking for
// this is just one way of finding it
NSInteger desiredWidth = 640;
AVCaptureDeviceFormat *bestFormat;
for (AVCaptureDeviceFormat *format in supportedFormats) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions((CMVideoFormatDescriptionRef)[format formatDescription]);
if (dimensions.width <= desiredWidth) {
bestFormat = format;
}
}
[[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][0] lockForConfiguration:nil];
[[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][0] setActiveFormat:bestFormat];
[[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][0] unlockForConfiguration];
It is possible that there are other ways of fixing this issue but this is fixed it for me.
So I had this problem also, but using the raw AVCaptureVideoDataOutput instead of JPG.
The issue is that the session presets Low/Medium/High actually affects the capture device in some ways, for example framerate, but it won't change the hardware capture resolution - it will always capture at 1280x720. The idea, I think, is that a normal Quicktime output device will figure this out and add a scaling step to 640x480 (for example) if the session preset is to Medium.
But when using the raw outputs, they won't care about the preset desired dimensions.
The solution, contrary to Apples documentation on videoSettings, is to add the requested dimensions to the videoSettings:
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:640], (id)kCVPixelBufferWidthKey,
[NSNumber numberWithDouble:480], (id)kCVPixelBufferHeightKey,
[NSNumber numberWithInt:kCMPixelFormat_422YpCbCr8_yuvs], (id)kCVPixelBufferPixelFormatTypeKey,
nil];
[captureoutput setVideoSettings:outputSettings];
I say contrary to Apples docs, because the docs say that the FormatTypeKey is the only key allowed here. But the bufferheight/width keys actually do work and are needed.

Objective c - How to programmatically set UIImage DPI of JPEG file while saving it

I have read that you can change the meta data of an image to set the dpi to another value other than the default 72. I tried the solution in this question but had the same problem as the author of that question. The image metadata properties in the original image seems to take precedence over modifications.
I am using the ALAssetsLibrary to write an image to the photo library on my iPhone. I need to dpi to be 500 instead of the standard 72dpi. I know it is possible to change the properties by manipulating the bits directly (as shown here), but am hoping that iOS gives a better solution.
Thank you in advance for your help.
This piece of code for manipulating image metadata -if exists- and you can use this to change any values in image meta data. Please be aware changing DPI value in metadata does not actually process image and change DPI.
#import <ImageIO/ImageIO.h>
-(NSData *)changeMetaDataInImage
{
NSData *sourceImageData = [[NSData alloc] initWithContentsOfFile:#"~/Desktop/1.jpg"];
if (sourceImageData != nil)
{
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)sourceImageData, NULL);
NSDictionary *metadata = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
NSMutableDictionary *tempMetadata = [metadata mutableCopy];
[tempMetadata setObject:[NSNumber numberWithInt:300] forKey:#"DPIHeight"];
[tempMetadata setObject:[NSNumber numberWithInt:300] forKey:#"DPIWidth"];
NSMutableDictionary *EXIFDictionary = [[tempMetadata objectForKey:(NSString *)kCGImagePropertyTIFFDictionary] mutableCopy];
[EXIFDictionary setObject:[NSNumber numberWithInt:300] forKey:(NSString *)kCGImagePropertyTIFFXResolution];
[EXIFDictionary setObject:[NSNumber numberWithInt:300] forKey:(NSString *)kCGImagePropertyTIFFYResolution];
NSMutableDictionary *JFIFDictionary = [[NSMutableDictionary alloc] init];
[JFIFDictionary setObject:[NSNumber numberWithInt:300] forKey:(NSString *)kCGImagePropertyJFIFXDensity];
[JFIFDictionary setObject:[NSNumber numberWithInt:300] forKey:(NSString *)kCGImagePropertyJFIFYDensity];
[JFIFDictionary setObject:#"1" forKey:(NSString *)kCGImagePropertyJFIFVersion];
[tempMetadata setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyTIFFDictionary];
[tempMetadata setObject:JFIFDictionary forKey:(NSString *)kCGImagePropertyJFIFDictionary];
NSMutableData *destinationImageData = [NSMutableData data];
CFStringRef UTI = CGImageSourceGetType(source);
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destinationImageData, UTI, 1, NULL);
CGImageDestinationAddImageFromSource(destination, source,0, (__bridge CFDictionaryRef) tempMetadata);
CGImageDestinationFinalize(destination);
return destinationImageData;
}
}
While trying to employ this solution for exporting JPEGs, I found that Photoshop wouldn't honour the DPI as exported. It turns out that Photoshop actively ignores the JFIF header information (and, in fact, doesn't even export it itself when it writes out JPEGs).
I ditched the JFIF section entirely. Most modern image libraries look at the TIFF/EXIF data first, so including that (and only that) seems to work well. Your mileage may vary, so be sure to test as many export destinations as you can!
However, the TIFF data is missing the ResolutionUnit tag, without which Photoshop will ignore XResolution and YResolution. It can have three values:
1 = No absolute unit of measurement. Used for images that may have a non-square aspect ratio, but no meaningful absolute dimensions.
2 = Inch.
3 = Centimeter.
Setting ResolutionUnit to 2 is required for DPI (dots per inch), for example:
[EXIFDictionary setObject:#(2) forKey:(NSString *)kCGImagePropertyTIFFResolutionUnit];

How to add NSArray in a video with AVMutableMetadataItem?

I am trying to save video with custom NSArray metadata relevant for my app and trying to retrieve it when the user selects that video from the library.
I'm using the AVAssetExportSession function to add metadata.
I used the sample code AVMovieExporter, and I tried to change locationMetadata.value
http://developer.apple.com/library/ios/#samplecode/AVMovieExporter/Introduction/Intro.html#//apple_ref/doc/uid/DTS40011364
AVMutableMetadataItem *locationMetadata = [[AVMutableMetadataItem alloc] init];
locationMetadata.key = AVMetadataCommonKeyLocation;
locationMetadata.keySpace = AVMetadataKeySpaceCommon;
locationMetadata.locale = self.locale;
//locationMetadata.value = [NSString stringWithFormat:#"%+08.4lf%+09.4lf", self.location.coordinate.latitude, self.location.coordinate.longitude];
locationMetadata.value = [[NSArray alloc] initWithObjects: #"abc", #123,nil];
If I use value as a NSString there is no problem, but if I use a NSArray, it doesn't save the metadata.
Where is the problem?
Apple's documentation states:
NSString *const AVMetadataCommonKeyLocation;
I understand that to say the value must be a string, and cannot be an array.

How do i grab numbers from a Table on a website in my Cocoa App?

Ok this is a more specific version of my last question.
So on a website there exists some data that is coded in HTML into a table.
In my Cocoa app, I want to download the html code of the website and then read through the html and snag the data from it. I was hoping someone could point out some useful classes/methods for accomplishing the retrieval of the website and putting it into some format where I can read through the code in my program?
Thanks in advance!
Try using hpple, it's an HTML parser for ObjC.
here's an example using it:
#import "TFHpple.h"
NSData *data = [[NSData alloc] initWithContentsOfFile:#"example.html"];
// Create parser
xpathParser = [[TFHpple alloc] initWithHTMLData:data];
//Get all the cells of the 2nd row of the 3rd table
NSArray *elements = [xpathParser search:#"//table[3]/tr[2]/td"];
// Access the first cell
TFHppleElement *element = [elements objectAtIndex:0];
// Get the text within the cell tag
NSString *content = [element content];
[xpathParser release];
[data release];