Cocoa: AVAsset loaded from file has 0 tracks - objective-c

I'm attempting to concatenate some audio files using the technique shown here. My audio files are .m4a and I can verify that they play fine in Quicktime. Here's the code I'm trying to use to concatenate them:
[currFile.audioContent writeToFile:tempOldFilePath atomically:NO];
AVURLAsset *oldAudioAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:tempOldFilePath] options:nil];
AVURLAsset *newAudioAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:tempInputFilePath] options:nil];
NSLog(#"oldAsset num tracks = %lu",(unsigned long)oldAudioAsset.tracks.count);
NSLog(#"newAsset num tracks = %lu",(unsigned long)newAudioAsset.tracks.count);
AVAssetTrack *oldTrack = [[oldAudioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
AVAssetTrack *newTrack = [[newAudioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *error=nil;
[compTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, oldTrack.timeRange.duration) ofTrack:oldTrack atTime:kCMTimeZero error:&error];
if (error) {
NSLog(#"%#",error.localizedDescription);
error=nil;
}
[compTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, newTrack.timeRange.duration) ofTrack:newTrack
atTime:oldTrack.timeRange.duration error:&error];
if (error) {
NSLog(#"%#",error.localizedDescription);
error=nil;
}
exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetAppleM4A];
exporter.outputURL = [NSURL URLWithString:tempCompFilePath];
exporter.outputFileType = AVFileTypeAppleM4A;
[exporter exportAsynchronouslyWithCompletionHandler:^{
NSLog(#"handller");
NSError *error=nil;
NSData *newData = [NSData dataWithContentsOfFile:tempCompFilePath options:0 error:&error];
NSLog(#"%lu",(unsigned long)newData.length);
if (error) {
NSLog(#"%#",error.localizedDescription);
}
currFile.audioContent = newData;
[[AppDelegate sharedDelegate] saveAction:nil];
}];
The first problem I noticed is that the exporter's handler method is never called. I'm guessing the reason for this is the other problem I noticed: After created my AVAssets from URL, log statements show that they contain 0 tracks. Apple's example doesn't exactly show how the AVAssets are loaded.
Any advice on how to get this working?

As you've already found, you need to use fileURLWithPath:, not URLWithString:, to create your URLs.
URLWithString: expects a string that describes a URL, such as #"file:///path/to/file" or #"http://example.com/". When your string describes a path alone, such as #"/path/to/file", you must use fileURLWithPath:, which will fill in the missing pieces correctly.
More technically, URLWithString: will interpret a path as simply a URL with only a path but no particular scheme, which you could go on to use relative to a base URL in any file-oriented scheme, such as HTTP (GET /path/to/file). fileURLWithPath: will interpret a path as a local file path, and return a file: URL accordingly.

I found the reason why this error was occurred and eventually solve it.
Originally I had set the source file path as ".mp4".
But the type of recorded video file was MOV so I changed as ".mov".
NSString *source_file_path = #"temp_video.mov"
instead of
NSString *source_file_path = #"temp_video.mp4"
The problem was fixed and it is working well now.
Hope to be helpful for all.

Apparently I was using the wrong NSURL method. I changed it to this:
AVURLAsset *oldAudioAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:tempOldFilePath] options:nil];
Now my AVAssets each have 1 track in them. If anyone can provide a good explanation as to why this is necessary, I will accept that answer.

Related

Everytime same video gets uploaded + Assets Library

ImgVidData =[[NSData alloc]init];
ALAssetRepresentation *representation = alAsset.defaultRepresentation;
NSURL *movieURL = representation.url;
NSURL *uploadURL = [NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:#"test"] stringByAppendingString:#".mov"]];
// ImgVidData = [NSData dataWithContentsOfFile: [[NSTemporaryDirectory() stringByAppendingPathComponent:#"test"] stringByAppendingString:#".mp4"]];
AVAsset *asset = [AVURLAsset URLAssetWithURL:movieURL options:nil];
AVAssetExportSession *session =
[AVAssetExportSession exportSessionWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
session.outputFileType = AVFileTypeQuickTimeMovie;
session.outputURL = uploadURL;
videoURL=uploadURL;
[session exportAsynchronouslyWithCompletionHandler:^{
if (session.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"output Video URL %#",uploadURL);
}
}];
ImgVidData = [NSData dataWithContentsOfURL:session.outputURL];
Above is the code , i have implemented inside didFinishPickingAssets:(NSArray *)assets
I'm picking up the video and uploading the video to server. I'm fetching uploaded video URL and converting it into NSdata and uploading it to server.
STRANGE : everytime I'm getting same video uploaded to server which i choose first time. i choose different video everytime but same videos get uplaoded.
Any help !!

AVFoundation to reproduce a video loop

I need to reproduce a video indefinitely (restarting the video when it ends) in my OpenGL application.
To do so I'm trying to utilize AV foundation.
I created an AVAssetReader and an AVAssetReaderTrackOutput and I utilize the copyNextSampleBuffer method to get CMSampleBufferRef and create an OpenGL texture for each frame.
NSString *path = [[NSBundle mainBundle] pathForResource:videoFileName ofType:type];
_url = [NSURL fileURLWithPath:path];
//Create the AVAsset
_asset = [AVURLAsset assetWithURL:_url];
//Get the asset AVAssetTrack
NSArray *arrayAssetTrack = [_asset tracksWithMediaType:AVMediaTypeVideo];
_assetTrackVideo = [arrayAssetTrack objectAtIndex:0];
//create the AVAssetReaderTrackOutput
NSDictionary *dictCompressionProperty = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id) kCVPixelBufferPixelFormatTypeKey];
_trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:_assetTrackVideo outputSettings:dictCompressionProperty];
//Create the AVAssetReader
NSError *error;
_assetReader = [[AVAssetReader alloc] initWithAsset:_asset error:&error];
if(error){
NSLog(#"error in AssetReader %#", error);
}
[_assetReader addOutput:_trackOutput];
//_assetReader.timeRange = CMTimeRangeMake(kCMTimeZero, _asset.duration);
//Asset reading start reading
[_assetReader startReading];
And in -update method of my GLKViewController I call the following:
if (_assetReader.status == AVAssetReaderStatusReading){
if (_trackOutput) {
CMSampleBufferRef sampleBuffer = [_trackOutput copyNextSampleBuffer];
[self createNewTextureVideoFromOutputSampleBuffer:sampleBuffer]; //create the new texture
}
}else if (_assetReader.status == AVAssetReaderStatusCompleted) {
NSLog(#"restart");
[_assetReader startReading];
}
All work fine until the AVAssetReader is in the reading status but when it finished reading and I tried to restart the AVAssetReading with a new call [_assetReader startReading], the application crash without output.
What I'm doing wrong? It is correct to restart an AVAssetReading when it complete his reading?
AVAssetReader doesn't support seeking or restarting, it is essentially a sequential decoder. You have to create a new AVAssetReader object to read the same samples again.
Thanks Costique! Your suggestion brings me back on the problem. I finally restarted the reading by creating a new AVAssetReader. However in order to do that I noted that a new AVAssetReaderTrackOutput must be created and added to the new AVAssetReader
e.g.
[newAssetReader addOutput:newTrackOutput];

Asset loading failing (Cocoa for OS X)

I'm very new to mac development and I have a hard time understanding how all this Cocoa stuff works.
Right now I'm working on getting still images from a video file and in order to do so, I need to load my video file (asset).
NSURL *url = [[NSURL alloc]initWithString:#"/Users/EVG/Desktop/myfile.mp4"];
AVURLAsset *myAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = #[#"tracks"];
[myAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^()
{
NSError *error = nil;
AVKeyValueStatus tracksStatus = [myAsset statusOfValueForKey:#"tracks" error:&error];
if ( tracksStatus == AVKeyValueStatusFailed ||
tracksStatus == AVKeyValueStatusCancelled )
{
NSLog(#"Failed with error: %#", [error localizedDescription]);
}
}];
After this code is executed I'm getting the following message:
NSView Controller[644:4413] Failed with error: The operation could not be completed
Does anyone know how to solve this problem I'm having?
Thank you!!!
Your URL is wrong. First of all, absolute file paths should begin with a /, as in /Users/EVG/Desktop/myfile.mp4. However, that's not a URL. A file URL should be file:///Users/EVG/Desktop/myfile.mp4.

How to write items to a directory and call them, Objective c

Hi I have a bunch of photos that I download from the web and I want to store them in a directory so I dont have to reload photos already downloaded, I am having trouble understanding how exactly to do this and store the photos and reload them from the directory and such. This is what I have so far. Could someone please explain the steps involved and how to do it? Thanks
-(void) savePhotoInCache: (NSData*) photoToSave{
//I dont know if u need bundle ID?
// NSString * bundleID = [[NSBundle mainBundle] bundleIdentifier];
NSFileManager *fm = [[NSFileManager alloc] init];
NSArray * directoryPaths = [fm URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask];
NSLog(#"%#", directoryPaths);
NSURL* dirPath = nil;
//Does this create a file in my cache Directory to store my photos?
dirPath = [[directoryPaths objectAtIndex:0] URLByAppendingPathComponent:[NSString stringWithFormat:#"photos.jpg"]];
NSError* theError = nil;
[fm createDirectoryAtURL:dirPath withIntermediateDirectories:YES attributes:nil error:&theError];
// Saves the photo to the file?
[photoToSave writeToURL: dirPath atomically:NO];
NSLog(#"%#", dirPath);
//I get a deprecated warning, new version needs encoding, but I did not specify encoding in writeToURL so what do I use?
NSString * contents = [NSString stringWithContentsOfURL:dirPath ];
//After this how to I access my files and check what the contents of the file are? also, how do I limit the amount of information it stores? thanks
}
check this to how to get saved contents like this:
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:nil];
Now filter contains like this:
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.jpg'"];
NSPredicate *fltr1 = [NSPredicate predicateWithFormat:#"self ENDSWITH '.png'"];
NSArray *onlyJPGs = [dirContents filteredArrayUsingPredicate:fltr];
NSArray *onlyPNGs = [dirContents filteredArrayUsingPredicate:fltr1];
To get all image content
for(NSString *fileName in dirContents)
{
NSString *imgPath = [dirPath stringByAppendingFormat:fileName];
UIImage *img = [UIImage imageWithContentsOfFile:imgPath];
//You have image use it where u want
}
If u have many images then use lazy loading of images
You are not required to handle everything by yourself. I would rather use EGO Image Loader which does pretty everything for you. it saves (cached) images in application bundle directory and reuse them whenever you need it. As far as study the classes, it uses the image url as primary key (to retrieve the image from cache).
Its pretty fast and Asynchronous.
Just add it to your project and call it like this:(This is ARC, so I don't use -release)
UIImageView *imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageNamed:#"placeholder.png"]];
imageView.frame = CGRectMake(0.0f, 44.0f, 320.0f,54.0f);
imageView.imageURL = [NSURL URLWithString:#"www.example.com/1.jpg"] ;
[self.view addSubview:imageView];
I hope it's useful.

How to tell if an iOS application has been newly installed or updated?

I have an application currently on the app store which I intend to submit an update for soon.
With this update I want to add code which will tell the app when it first runs application:didFinishLaunchingWithOptions whether it is:
A new install from the app store.
Newly updated from a previous version
There is no code in the app currently in the app store to handle this.
The application uses a SQLite database, but for reasons I won't go into here I don't want to use a check for its existence as a solution to this problem.
As a side question, without storing the data manually, is there an SDK I can use to query when an app was installed onto a device? (Preferably iOS 3.0 compatible)
I have seen a similar question, but none of the answers apply to working with existing app store code.
The following code may help to answer your side question about when an app was installed. I am unsure if the app bundle create date is the XCode build date or the download date as this is untested from app store.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath]; // e.g. /var/mobile/Applications/<GUID>/<AppName>.app
NSFileManager *manager = [NSFileManager defaultManager];
NSDictionary* attrs = [manager attributesOfItemAtPath:bundleRoot error:nil];
NSLog(#"Build or download Date/Time of first version to be installed: %#", [attrs fileCreationDate]);
NSLog(#"Date/Time of last install (unless bundle changed by code): %#", [attrs fileModificationDate]);
NSString *rootPath = [bundleRoot substringToIndex:[bundleRoot rangeOfString:#"/" options:NSBackwardsSearch].location]; // e.g /var/mobile/Applications/<GUID>
attrs = [manager attributesOfItemAtPath:rootPath error:nil];
NSLog(#"Date/Time first installed (or first reinstalled after deletion): %#", [attrs fileCreationDate]);
You could save a version number to NSUserDefaults, and update it accordingly.
If that won't work, you may be able to release an intermediate version which introduces the versioning scheme.
If that's not an option, you may be able to check for traces of previous runs from files you create, or preferences which you set conditionally or lazily.
try this code, i know i am too late for this answer but for knwoldge sharing here i go.
-(void) checkIsAppUpdated
{
NSString *urlString = #"http://itunes.apple.com/lookup?id=995558215";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"GET"];
NSError *error = nil;
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//NSString *stringReply = (NSString *)[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (error)
{
// NSLog(#"Error: %#", stringReply);
//error reviced
}
else
{
//The response is in data
//NSLog(#"Success: %#", stringReply);
NSDictionary *dictResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
float appStoreVersion=[[[[dictResponse objectForKey:#"results"] firstObject] objectForKey:#"version"] floatValue];
NSLog(#"app stroe version=%f",appStoreVersion);
NSString *strLocalVersion=[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
float localAppVersion=[strLocalVersion floatValue];
if (localAppVersion!=appStoreVersion)
{
//open app store url
// NSString *iTunesLink = #"itms://itunes.apple.com/us/app/apple-store/id375380948?mt=8";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"https://itunes.apple.com/app/washthenfold/id995558215?mt=8"]];
}
}
}
note
id:-replace id with your own app id. you can get app id from itune connect selected app->more info->meta data
since simulator doesn't have app store app, this code won't work on simulator.
GBVersionTracking is good pod to track all version history.
[GBVersionTracking isFirstLaunchEver];
Here is a simple code to know if the current version is different (this code work on simulator too.)
-(BOOL) needsUpdate
{
NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString* appID = infoDictionary[#"CFBundleIdentifier"];
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"http://itunes.apple.com/lookup?bundleId=%#", appID]];
NSData* data = [NSData dataWithContentsOfURL:url];
NSDictionary* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([lookup[#"resultCount"] integerValue] == 1)
{
NSString* appStoreVersion = lookup[#"results"][0][#"version"];
NSString* currentVersion = infoDictionary[#"CFBundleShortVersionString"];
if (![appStoreVersion isEqualToString:currentVersion])
{
NSLog(#"Need to update [%# != %#]", appStoreVersion, currentVersion);
return YES;
}
}
return NO;
}
Note: Make sure that when you enter the new version in iTunes, this matches the version in the app you are releasing. If not then the above code will always return YES regardless if the user updates.