This was working fine in iOS 6, but in iOS 7, after I export a portion of a song using AVAssetExportSession to a file, the exported file's duration is wrong in AVAudioPlayer, but correct in AVURLAsset.
AVAudioPlayer incorrectly reports the duration as the whole song duration.
I'm exporting files using steps from https://developer.apple.com/library/ios/qa/qa1730/_index.html
and checking the durations as below:
AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:outputURL options:nil];
CMTime audioDuration = audioAsset.duration; // shows correct
and
AVAudioPlayer* avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:outputURL error:nil];
NSTimeInterval duration = avAudioPlayer.duration; // shows wrong
Interestingly, if I play the exported file in iTunes, it also shows the wrong (whole) duration.
I'm not sure how to fix this problem. Could this be a bug in iOS7?
Related
Essentially I am looking to concatenate AVAsset files. I've got a rough idea of what to do but I'm struggling with loading the audio files.
I can play the files with an AVAudioPlayer, I can see them in the directory via my terminal, but when I attempt to load them with AVAssetURL it always returns an empty array for tracks.
The URL I am using:
NSURL *firstAudioFileLocation = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#%#", workingDirectory , #"/temp.pcm"]];
Which results in:
file:///Users/evolve/Library/Developer/CoreSimulator/Devices/8BF465E8-321C-47E6-BF2E-049C5E900F3C/data/Containers/Data/Application/4A2D29B2-E5B4-4D07-AE6B-1DD15F5E59A3/Documents/temp.pcm
The asset being loaded:
AVAsset *test = [AVURLAsset URLAssetWithURL:firstAudioFileLocation options:nil];
However when calling this:
NSLog(#" total tracks %#", test.tracks);
My output is always total tracks ().
My subsequent calls to add them to my AVMutableCompositionTrack end up crashing the app as the AVAsset seems to not have loaded correctly.
I have played with other variations for loading the asset including:
NSURL *alternativeLocation = [[NSBundle mainBundle] URLForResource:#"temp" withExtension:#"pcm"];
As well as trying to load AVAsset with the options from the documentation:
NSDictionary *assetOptions = #{AVURLAssetPreferPreciseDurationAndTimingKey: #YES};
How do I load the tracks from a local resource, recently created by the AVAudioRecorder?
EDIT
I had a poke around and found I can record and load a .CAF file extension.
Seems .PCM is unsupported for AVAsset, this page also was of great help. https://developer.apple.com/documentation/avfoundation/avfiletype
An AVAsset load is not instantaneous. You need to wait for the data to be available. Example:
AVAsset *test = [AVURLAsset URLAssetWithURL:firstAudioFileLocation options:nil];
[test loadValuesAsynchronouslyForKeys:#[#"playable",#"tracks"] completionHandler:^{
// Now tracks is available
NSLog(#" total tracks %#", test.tracks);
}];
A more detailed example can be found in the documentation.
Right now, I'm using MNavChapters to get the chapter metadata for audio files and using MPMediaPlayerController to play the audio files.
This works great until I try to load an Audible (AA) book's chapters. The MPMediaItemPropertyAssetURL returns nil because this is a "protected" file. Is there an alternative to read the chapter metadata?
Current non-working code:
NSURL *assetURL = [self.mpmediaitem valueForProperty:MPMediaItemPropertyAssetURL]; //this is null :(
AVAsset *asset = [AVAsset assetWithURL:assetURL];
MNAVChapterReader *reader = [MNAVChapterReader new];
NSArray *chapters = [reader chaptersFromAsset:asset];
I just noticed that this works in iOS 9
I use AVAudioPlayer for playing audio file and UISlider to show user current time. Firstly, it looked that everything is fine but I noticed that audio player returns wrong file duration. For example it returns me duration equals to 3.5sec but file durations is equal to 6 sec. in reality.
Do you know What can cause this problem?
Below you can see my code which return file duration:
- (NSTimeInterval)audioDurationAtURL:(NSURL *)url
{
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url];
_audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:&error];
return _audioPlayer.duration;
}
To add a bit to TonyMkenu's answer, AVAsset is an alternative with the ability to give you a more accurate duration.
https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVAsset_Class/Reference/Reference.html#//apple_ref/occ/instp/AVAsset/duration
If you specify providesPreciseDurationAndTiming = YES, then AVAsset will decode the file if needed to determine its duration with accuracy. If the decode time is too long for your use, you can disable it.
In my situation, I use the AVURLAsset subclass:
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:localURL options:[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], AVURLAssetPreferPreciseDurationAndTimingKey, nil]];
float t = CMTimeGetSeconds(asset.duration);
AVAudioPlayer appears to only returns the correct duration of a file when it is ready for play it, so try to check the length of the audio file after [_audioPlayer play];
Under certain compression formats, the audio player may change its estimate of the duration as it learns more and more about the song by playing (and hence decoding) more and more of it - https://stackoverflow.com/a/16265186
In my case, having added an audio file to a project then editing (making it longer or shorter) and then deleting and re-adding the file to the Xcode project was the problem.
Essentially the project is caching the old file. To debug this I renamed the audio file to something else, added the new audio file to the project after which the duration reported by the player was always correct, before and after calling play.
I have the following code that used to work to play a sound in my project however after iv done some playing around over the past few weeks on other aspects of the project it dose not seem to play.
The audio file is in the main directory and works in preview.
Any ideas?
//playsound
AVAudioPlayer *showsound;
NSString *audiopath = [[NSBundle mainBundle] pathForResource:#"mouse1" ofType:#"wav"];
NSURL *audiourl = [NSURL fileURLWithPath:audiopath];
showsound = [[AVAudioPlayer alloc]initWithContentsOfURL:audiourl error:Nil];
[showsound play];
It's because after the line showsound play your method comes to an end and showsound (your AVAudioPlayer) is released. Hence there is nothing to do any playing any more. Solution: retain it! (For example, set an instance variable to hold on to it.)
We're trying to take an existing video with audio (.mov) and make a more email friendly version. Seems pretty straightforward and the code below does just what we need ... almost.
On an iPad2 (4.3.3) it works in debug & release builds all of the time. On the iPhone 4 (4.3.3) or 4th gen iPod Touch there's no audio. From time to time, no obvious correlation as to what triggers it, it will start working on the iPhone. Delete the app, rebuild/install, and it no longer works.
AVURLAsset* asset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:sourcePath] options:nil];
session = [[AVAssetExportSession alloc] initWithAsset:asset
presetName:AVAssetExportPresetLowQuality];
session.outputURL = [NSURL fileURLWithPath:destPath];
session.outputFileType = AVFileTypeQuickTimeMovie;
session.shouldOptimizeForNetworkUse = YES;
[session exportAsynchronouslyWithCompletionHandler:^{
[self performSelectorOnMainThread:#selector(conversionFinished)
withObject:nil
waitUntilDone:NO]; }];
Are you playing the movie too, for example in an MPMoviePlayer? I have had some occasional export quirks while playing or using other initialized assets with the same URLs.