iPhone: get duration of an audio file - iphone

What is the easiest way to get a duration of an audio file?
I could create an object of AVAudioPlayer, initialize it with URL and than get the duration, but this way is too long. Is there an easier way?
Thanks.

Correct code is
NSURL *afUrl = [NSURL fileURLWithPath:soundPath];
AudioFileID fileID;
OSStatus result = AudioFileOpenURL((CFURLRef)afUrl, kAudioFileReadPermission, 0, &fileID);
Float64 outDataSize = 0;
UInt32 thePropSize = sizeof(Float64);
result = AudioFileGetProperty(fileID, kAudioFilePropertyEstimatedDuration, &thePropSize, &outDataSize);
AudioFileClose(fileID);
outDataSize should be Float64 not UInt64.

You can use the Audio File Services functions. There's one property to get that should give you the estimated duration. Code:
NSURL *afUrl = [NSURL fileURLWithPath:soundPath];
AudioFileID fileID;
OSStatus result = AudioFileOpenURL((CFURLRef)afUrl, kAudioFileReadPermission, 0, &fileID);
Float64 outDataSize = 0;
UInt32 thePropSize = sizeof(Float64);
result = AudioFileGetProperty(fileID, kAudioFilePropertyEstimatedDuration, &thePropSize, &outDataSize);
AudioFileClose(fileID);
You can check the docs here

If you know anything about the audio file in question (samplerate, bitdepth, channel count), and it's an uncompressed format (WAV/AIFF), then you can calculate the /approximate/ duration from the filesize:
length_in_seconds = (file_length-guess_100_bytes_for_header) / (samplerate*(bitdepth*channel_count/8))

It depends on the file type. If it's a WAV file you can locate the file's header and determine the playback duration that way. If it's a compressed format (*.mp3 etc.) you're better off sticking to the method you mentioned.

Combining AVAudioPlayer with Swift becomes as easy as (I am migrating a Realm table below, but you get the idea):
import AVFoundation
let resource = old!["filename"] as? String
let afUrl = NSBundle.mainBundle().URLForResource(resource, withExtension: nil)
let player = try! AVAudioPlayer(contentsOfURL: afUrl!)
new!["duration"] = Double(player.duration)

In case anyone lands here looking for a way to get the duration for both audio and video files, have a look at this answer to another post, which instead uses AVAsset instead:
https://stackoverflow.com/a/7052147/381233
Using AudioFileGetProperty (like the other two main answers here) to get the duration of A/V files didn't work for a few .mov files on my device (result was always 0), while the solution using AVAsset got the duration for all audio and video files on my device.
(Interestingly enough, however, the duration from both solutions was sometimes 1 second more than that displayed in the UI of an actual AVAudioPlayer. Most likely the AVAudioPlayer uses a non-standard rounding routine for the displayed duration.)

AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:mp3_url options:nil];
[audioAsset loadValuesAsynchronouslyForKeys:#[#"duration"] completionHandler:^{
CMTime audioDuration = audioAsset.duration;
float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
NSLog(#"duration:%f",audioDurationSeconds);
}];

sample code from answer How to get the duration of an audio file in iOS?. This is the best answer.
AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:audioFileURL options:nil];
CMTime audioDuration = audioAsset.duration;
float audioDurationSeconds = CMTimeGetSeconds(audioDuration);

Related

AVMutableVideoComposition some inserted video crash composition

Some videos play fine alone as simple AVAssets but when inserted in an AVMutableVideoComposition they make the whole composition fail even if those videos are inserted alone in an empty composition. Anyone has similar issues? Sometime re-encoding the videos before inserting them makes it work sometime not. I can't see any mistake in my timing instruction and using some other video don't cause any problem at all no matter their length. Can there be issues with the number of frame or the duration of the asset or their format? (all are h264 single track)
Well indeed it looks like some video may cause problem when being inserted. example here duration of the track is the issue.
AVAsset * video = ...;
NSArray * videoTracks = [video tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack * videoTrack = videoTracks.firstObject;
CMTime duration = videoTrack.timeRange.duration // this time cause error
AVMutableCompositionTrack * track = ...;
CMTime insertCMTime = kCMTimeZero;
CMTimeRange trackRange = CMTimeRangeMake(kCMTimeZero, duration);
[track insertTimeRange:trackRange
ofTrack:videoTrack
atTime:insertCMTime
error:nil];
Trimming the range to insert to a round second solved the problem for all video tested so far
NSTimeInterval interval = (int)CMTimeGetSeconds(videoTrack.timeRange.duration);
CMTime roundedDuration = CMTimeMakeWithSeconds(interval, 60000);

iOS6 AVAudioPlayer - AAC audio file duration always return 0

I'm porting an app that works with aac audio files to iOS6 and I've found an strange behavior, when I try to get the duration of the (valid) aac audio file, it's always returns 0, in iOS4 and iOS5 it works fine.
¿Is there any bug on AvAudioPlayer class that affects duration property? I have read about some troubles with the currentTime property.
Here's the code:
NSURL* urlFichero = [NSURL fileURLWithPath:rutaFichero];
avaPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: urlFichero error:nil];
segundos = avaPlayer.duration;
NSLog(#"[ControladorFicheros] Fichero: '%#' Duración: '%f'", nombreFichero, segundos);
[avaPlayer stop];
[avaPlayer release];
Thanks ;)
Finally the problem is that in newest versions of the API, AVAudioPlayer appears to only returns the correct duration of a file when it is ready for play it, that's why my solution was wrong, the correct way to get the duration of a file (in seconds) if you don't want to reproduce it is:
AVURLAsset *asset = [[[AVURLAsset alloc] initWithURL:anURI_ToResource
options:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
AVURLAssetPreferPreciseDurationAndTimingKey,
nil]] autorelease];
NSTimeInterval durationInSeconds = 0.0;
if (asset)
durationInSeconds = CMTimeGetSeconds(asset.duration) ;
Swift
let asset = AVURLAsset(url: url, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
let durationInSeconds = CMTimeGetSeconds(asset.duration)
I noticed the same problem. My solution is to use instead.
MPMoviePlayerController *testPlayer = [[MPMoviePlayerController alloc] initWithContentURL:filePath];
[testPlater prepareToPlay];
[testPlater play];

Cropping videos in iOS

We can crop images. Can we crop videos?
Since video is a collection of pictures you can crop all frames from video and after create new video. AVFoundation guide describe some tasks:
Putting it all Together: Capturing Video Frames as UIImage Objects
After this you crops images and write video
You can use an asset writer to produce a QuickTime movie file or an
MPEG-4 file from media such as sample buffers or still images.
See for more details AV Foundation Framework
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
exportSession.outputURL = outputURL;
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(120.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void){
handler(exportSession);
[exportSession release];}];
Here we get a video of first 2 mins.
You should be able to do this using AVAssetExportSession, AVVideoComposition, and AVVideoCompositionCoreAnimationTool (and just set up a CALayer hierarchy with the positioning you want). I'm not sure if this is the most efficient way, though.
it's not as simple as images
but it could be as easy as the correct specification of the video but there is not enough information.
in the decoding settings, you can manipulate video pixels by geometry, ie, anamorphic, squeezed, stretched and also player/browser settings, the image window or player window, you can specify a small player window and a magnification level. if you allow or disallow zoom/magnification, you'll force an offscreeen draw or black bars.
i would encode to the correct size and platform for best quality, these kinds of fixes are 'kludges' but they work in a pinch. i would grab the quicktime sdk and poke around.

How to play movie files with no file extension on iOS with MPMoviePlayerController or AVPlayer?

I want to play a movie in iOS 4.3 on the iPad. I've successfully used MPMoviePlayerController and AVPlayer to load files from a remote URL when the filename has a file extension. However, when I use a CDN that doesn't return the filename (just an un-guessable random name), neither MPMoviePlayerController or AVPlayer seem to be able to cope.
Is there a way to tell either player that it really is a movie of type x and it should just get on playing it?
MPMoviePlayerController will return the following error from it's changed state notification:
{
MPMoviePlayerPlaybackDidFinishReasonUserInfoKey = 1;
error = "Error Domain=MediaPlayerErrorDomain Code=-12847 \"This movie format is not supported.\" UserInfo=0x5b60030 {NSLocalizedDescription=This movie format is not supported.}";
}
I know that file is a valid m4v file, as when I rename it all is fine.
File at tmp
NSString* _filePath
Create symlink
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *slink = [_filePath stringByAppendingPathExtension:#"m4v"];
if (![filemgr fileExistsAtPath:slink]) {
NSError *error = nil;
[filemgr createSymbolicLinkAtPath:[_filePath stringByAppendingPathExtension:#"m4v"] withDestinationPath: _filePath error: &error];
if (error) {
...
}
}
...
play video by slink
If the player can't guess the file format you need to check that the CDN sends the right mime type back. My guess is that your CDN can't guess the mimetype correctly nor can the player.
In most cases this is due to how the CDN presents the HTTP header. Check that the "Content-Type" header is set to a video format matching your content.
WebKit handle this by a Private AVURLAsset option: AVURLAssetOutOfBandMIMETypeKey, this option is used when you specify a MIME type in the HTML's video tag,
You can use this option like:
NSString * mimeType = #"video/mp4";
// or even with codecs
mimeType = #"video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"";
// create asset
AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:url options:#{#"AVURLAssetOutOfBandMIMETypeKey": mimeType}];
// create AVPlayer with AVURLAsset
AVPlayer * player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:asset]];
Since it is a private key, you may want to obfuscate it if you plan to submit it to AppStore.
The WebKit source can be found here:
https://opensource.apple.com/source/WebCore/WebCore-7604.1.38.1.6/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm.auto.html
Finally, I found the answer.
You should use AVURLAsset (the subclass of AVAsset) and set the MIMEType in the options input :
let mimeType = "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
let urlAsset = AVURLAsset(url: url, options: ["AVURLAssetOutOfBandMIMETypeKey": mimeType])
Source -> https://stackoverflow.com/a/54087143/6736184
iPhone support video H.264, MPEG-4 in .mp4, .m4v, .mov formats and audio files in AAC, MP3, M4a, Apple lossless and Audible.
You can use NSFileManager's -contentsOfDirectoryAtPath:error: method to get an array with the contents of a directory (as strings).Then you just do strings operations .
Dylan is correct.
Both MPMoviePlayer and AVPlayer needs a file extension in order to play the file from URL otherwise an error message will be shown. Better to use some kind of tricks.
If you have problems to get the ContentType of your connection you could cycle through the playable MIME types and create symbolic links to the actual file with the extension and check if they are playable. Like so:
NSLog(#"linked path: %#",[videoURL absoluteString]);
NSString* linkedPath;
AVURLAsset* asset;
NSFileManager *filemgr = [NSFileManager defaultManager];
for (NSString* string in [AVURLAsset audiovisualMIMETypes]) {
if ([string containsString:#"video/"]) {
NSLog(#"Trying: %#",string);
linkedPath = [[videoURL absoluteString] stringByAppendingPathExtension:[string stringByReplacingOccurrencesOfString:#"video/" withString:#""]];
NSLog(#"linked path: %#",linkedPath);
if (![filemgr fileExistsAtPath:linkedPath]) {
NSError *error = nil;
[filemgr createSymbolicLinkAtURL:[NSURL URLWithString:linkedPath] withDestinationURL:videoURL error:&error];
if (error) {
NSLog(#"error %#",error.localizedDescription);
}
}
asset = [AVURLAsset assetWithURL:[NSURL URLWithString:linkedPath]];
if ([asset isPlayable]) {
NSLog(#"Playable");
break;
}else{
NSLog(#"Not Playable");
asset = nil;
}
}
}
It's sort of a hack, but what you could do is run each name through a method that checks for a period with three characters after it. If not, just append .m4v automatically. Or get the MIME type and append an extension automatically based on the returned type. If available. Look up documentation with NSString for more info. Good luck! Let me know if that helped.

How to find an audio file's length (in seconds)

(Objective C)
Just using simple AudioServicesPlaySystemSoundID and its counterparts, but I can't find in the documentation if there is already a way to find the length of an audio file.
I know there is AudioServicesGetPropertyInfo, but that seems to return a byte-buffer - do audio files embed their length in themselves and I can just extract it with this?
Or is there perhaps a formula based on bit-rate * fileSize to convert to length-of-time?
mIL3S
www.milkdrinkingcow.com
According to a quick Google search, there is a formula:
length-of-time (duration in seconds) = fileSize (in bytes) / bit-rate (bits/secs)*8
Is there any particular reason you're using System Sound Services to play a sound?
If you use AVAudioPlayer to handle your sounds you could do something like:
AVAudioPlayer * sound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:nil];
sound.delegate = self;
sound.volume = 1;
NSLog([NSString stringWithFormat:#"%f", sound.duration]);
sample code from answer How to get the duration of an audio file in iOS?. This is the best answer.
AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:audioFileURL options:nil];
CMTime audioDuration = audioAsset.duration;
float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
See at:
Float64 fileDuration = PrepareFileAU (fileAU, fileFormat, audioFile);
see ADC sample at:
http://developer.apple.com/library/mac/#samplecode/PlayFile/Introduction/Intro.html
without using AVPlayer...