I have been trying to extract metadata information from a .mp3 file for an iPhone app. I tried using AVAsset like this.It didn't work,the common meta data is empty. But audacity and another iOS app from app store could retrieve the meta data details. I don't know why?.
So, I tried to extract the same with below code using AudioToolBox framework
CFDictionaryRef piDict = nil;
UInt32 piDataSize = sizeof(piDict);
// Populates a CFDictionary with the ID3 tag properties
err = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
if(err != noErr) {
NSLog(#"AudioFileGetProperty failed for property info dictionary");
return nil;
}
// Toll free bridge the CFDictionary so that we can interact with it via objc
NSMutableDictionary* nsDict = [(__bridge NSDictionary*)piDict mutableCopy];
This returned everything except album art. When I tried to extract the album art using kAudioFilePropertyAlbumArtwork , I got osstatus error(The operation couldn't be completed).
So at last, I tried my luck with ObjC wrapper for libId3(found here). It worked perfectly well. I could get the artwork.
My question is, why AVAsset could not retrieve the data?. What am I missing there?. somebody managed to to get it work?. A sample will be appreciated.
Why kAudioFilePropertyAlbumArtwork extraction couldn't be completed?. Both the issues happened with all the .mp3 files I had.
Solution Update:
AVAsset didn't work for me because I made my URL using
[NSURL URLWithString:[filePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]
rather than
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
To use AVAsset to extract metadata informations, this post is useful. The following code is what you need:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"mp3"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
AVAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSArray *keys = [NSArray arrayWithObjects:#"commonMetadata", nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
for (AVMetadataItem *item in artworks) {
if ([item.keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSDictionary *dict = [item.value copyWithZone:nil];
self.imageView.image = [UIImage imageWithData:[dict objectForKey:#"data"]];
} else if ([item.keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
self.imageView.image = [UIImage imageWithData:[item.value copyWithZone:nil]];
}
}
}];
NSURL *fileURL1 = [NSURL fileURLWithPath:url];
AVAsset *asset = [AVAsset assetWithURL:fileURL1];
for (AVMetadataItem *metadataItem in asset.commonMetadata) {
if ([metadataItem.commonKey isEqualToString:#"artwork"]){
NSDictionary *imageDataDictionary = (NSDictionary *)metadataItem.value;
NSData *imageData = [imageDataDictionary objectForKey:#"data"];
UIImage *image = [UIImage imageWithData:imageData];
imageThumb.image = image;
}
}
Related
This is my code which is not working for any mp3 files:
//define AVassets
NSURL *assetUrl2 = [[NSURL fileURLWithPath:
[assetLoc stringByAppendingPathComponent:title]]
URLByAppendingPathExtension:ext]
AVAsset *asset2 = [AVURLAsset URLAssetWithURL:assetUrl2
options:nil];
//print asset url
NSString *encodedString = [assetUrl2 absoluteString];
NSLog(#"song name%#",encodedString); //this prints the valid url
NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatID3Metadata];
for (AVMetadataItem *item in origMetadata) {
NSLog(#"*** ID3 tags found");
NSString *keys = (NSString *)[item key];
NSString *value = [item stringValue];
NSLog(#"value for tag:%# is:%#", keys, value);
}
However, if I swap...
`NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatID3Metadata];`
with...
`NSArray *origMetadata =
[asset2 metadataForFormat:AVMetadataFormatiTunesMetadata];`
and load an AAC file rather than an MP3 file, it works. Obviously I am missing something here. Any help would be appreciated. Thank you.
I am writing an image in iPad using ALAssets. When it finish I try to create an UIImage with the returned URL but it won't load. This is the code:
LAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:[anImage CGImage] orientation:(ALAssetOrientation)[anImage imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if (!error) {
CGImageSourceRef src = CGImageSourceCreateWithURL((CFURLRef) [NSURL fileURLWithPath:[assetURL absoluteString]], NULL);
My purpose is to save an image to the device, then convert it to another format using ImageIO and finally send it to a web service. CGImageSourceRef is null, I also tried with standard UIImage with the same result.
What I am doing wrong here?
EDIT: The problem is when creating the CFURLRef.
If I do
CGImageSourceCreateWithURL((CFURLRef) assetURL, NULL);
I got this error
ImageIO: CGImageSourceCreateWithURL CFURLCreateDataAndPropertiesFromResource failed with error code -11.
But if I try to convert the URL with
[NSURL fileURLWithPath:[assetURL absoluteString]]
the path is changed to
assets-library:/asset/asset.JPG%3Fid=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG -- file://localhost/
I cannot find how to properly create the CFURLRef needed by the method. I tried printing all the conversions I could think of and this are the results
[assetURL relativePath]
[assetURL relativeString]
[assetURL absoluteURL]
[assetURL absoluteString]
/asset.JPG ,
assets-library://asset/asset.JPG?id=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG
assets-library://asset/asset.JPG?id=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG
assets-library://asset/asset.JPG?id=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG
[NSURL fileURLWithPath:[assetURL relativePath]]
[NSURL fileURLWithPath:[assetURL relativeString]]
[NSURL fileURLWithPath:[assetURL absoluteString]]
file://localhost/asset.JPG
assets-library:/asset/asset.JPG%3Fid=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG -- file://localhost/
assets-library:/asset/asset.JPG%3Fid=57BBBA99-E7BF-4DB7-839E-F915005E6DFA&ext=JPG -- file://localhost/
Help please, I am stuck with this :-(
This is what I did for my case.
UIImage* anImage; //this is the original image
NSData * imgData = UIImageJPEGRepresentation(anImage, 0.7);
CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef) imgData, NULL);
NSMutableData *data = [NSMutableData data];
CFStringRef imageType = CFSTR("com.microsoft.bmp");
CGImageDestinationRef myImageDest = CGImageDestinationCreateWithData((CFMutableDataRef) data, imageType, 1, nil);
//Convert!
CGImageDestinationAddImageFromSource(myImageDest, src, 0, myOptions);
CGImageDestinationFinalize(myImageDest);
//Freeing things
CFRelease(myImageDest);
CFRelease(src);
But this just converts the image, it doesn't store it in any file... Not sure this should be an answer to the original question.
If you already have an ALAsset and your goal is a CGImageRef you can do something like this.
ALAssetRepresentation* rep = [asset defaultRepresentation];
NSDictionary* options = [[NSDictionary alloc] initWithObjectsAndKeys:
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
(id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageAlways,
(id)[NSNumber numberWithDouble:400], (id)kCGImageSourceThumbnailMaxPixelSize,
nil];
CGImageRef image = [rep CGImageWithOptions:options];
Hi i am working on application in which , i have to upload a sound file to Facebook.
Please ,provide me a better solution, whether it is possible to share a sound file on Facebook or not.
Thanks in advance
Facebook does not have sound uploading. You could always upload the sound file elsewhere and use Facebook to share the link to it.
if you check the webApps for twitter/facebook, they does not provide any means to UPLOAD an audio file.
Twittier allows only text post and on the other hand, Facebook allow Image/Video to be uploaded.
In the light of these facts, I do not think it is possible without a url share.
It is not possible to upload audio files to Facebook, only photos and videos are allowed. However, another solution would be to upload the audio file somewhere else and then use the Facebook API to post a link using that reference. One place you may wish to look to upload audio is http://developers.soundcloud.com/
Use AVAssetExportSession, create a movie with the sound file and then upload it to Facebook.
This is possible to do but it is a bit of a pain. To do this you must convert the audio file into a video file and then post it to Facebook as a video.
First we need to have access to our audioFile, you should already have this, if not then there are a lot of Stackoverflow questions devoted to this, I won't complicate matters by going off track. We then create a NSURL to a video in our documents. In this case we have an video named video_base.mp4 which has been designed to be a nice background for our audio track. Finally we merge the files before sharing the returned file to Facebook.
- (IBAction)shareToFacebook:(id)sender {
// You should already have your audio file saved
NSString * songFileName = [self getSongFileName];
NSArray * searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentPath = [searchPaths objectAtIndex:0];
NSString * file = [documentPath stringByAppendingPathComponent:songFileName];
NSURL * audioFileURL = [NSURL fileURLWithPath: audioFile];
NSURL * videoFileURL = [NSURL fileURLWithPath:[NSFileManager getFilePath:#"video_base.mp4" withFolder:#""]];
[self mergeAudio:audioFileURL andVideo:videoFileURL withSuccess:^(NSURL * url) {
// Now we have the URL of the video file
[self shareVideoToFacebook:url];
}];
}
Credit to #dineshprasanna for this part of the code which can be found here. We want to merge our audio and video and then save them to a path. We then return the exportURL in the completion block.
- (void)mergeAudio: (NSURL *)audioURL andVideo: (NSURL *)videoURL withSuccess:(void (^)(NSURL * url))successBlock {
AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audioURL options:nil];
AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:videoURL options:nil];
AVMutableComposition * mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetHighestQuality];
NSString * videoName = #"export.mov";
NSString * exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName];
NSURL * exportUrl = [NSURL fileURLWithPath:exportPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
_assetExport.outputFileType = #"com.apple.quicktime-movie";
_assetExport.outputURL = exportUrl;
_assetExport.shouldOptimizeForNetworkUse = YES;
[_assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) {
if(successBlock) successBlock(exportUrl);
}];
}
Finally we want to save our return videoURL to Facebook. It is worth noting that we need a few libraries to be added for this functionality to work:
#import <AssetsLibrary/AssetsLibrary.h>
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
We then share the merged file to Facebook:
- (void)shareVideoToFacebook: (NSURL *)videoURL {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock = ^(NSURL *newURL, NSError *error) {
if(error) {
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
} else {
NSLog( #"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
FBSDKShareDialog *shareDialog = [[FBSDKShareDialog alloc]init];
NSURL *videoURL = newURL;
FBSDKShareVideo *video = [[FBSDKShareVideo alloc] init];
video.videoURL = videoURL;
FBSDKShareVideoContent *content = [[FBSDKShareVideoContent alloc] init];
content.video = video;
[FBSDKShareDialog showFromViewController:self
withContent:content
delegate:nil];
}
};
if([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:videoURL
completionBlock:videoWriteCompletionBlock];
}
}
This should open up the Facebook app and then allow the user to share their audio file on their wall with a background of the video stored in your app.
Obviously everyone's project is different, this means you might not be able to copy paste this code exactly into your project. I have tried to split up the process meaning it should be easy to extrapolate to get audio messages uploading successfully.
I've got the following question to ask:
How do you compile taglib with an iOS application?
I'm a bit confused as I added the folder into my project, tried to compile it, but instead it failed with 1640 errors.
How do I make it successfully compile - the reason why I ask is taglib allows for the extraction of album artwork from a tag.
If anyone knows an Objective-C based album artwork extraction class it would help - I don't know why Apple don't add a way of doing this in Core Foundation - because there are methods for extracting some of the data from an ID3 tag.
I can't see why there isn't some Objective-C way of doing it.
Any help appreciated.
EDIT:
After hours upon hours of trying to do this, I've found a Java function here that seems to do the job fine, but I haven't got a clue on how to convert it to Objective-C (or let alone C++ for that matter), as its types seem to be completely different from those that are in Objective-C/C++. If anyone knows of a way to convert this it would really help, as this seems as my last option as I've tried so many others.
- (void)loadArtworksForFileAtPath:(NSString *)path completion:(void (^)(NSArray *))completion
{
NSURL *u = [NSURL fileURLWithPath:path];
AVURLAsset *a = [AVURLAsset URLAssetWithURL:u options:nil];
NSArray *k = [NSArray arrayWithObjects:#"commonMetadata", nil];
[a loadValuesAsynchronouslyForKeys:k completionHandler: ^{
NSArray *artworks = [AVMetadataItem metadataItemsFromArray:a.commonMetadata
withKey:AVMetadataCommonKeyArtwork keySpace:AVMetadataKeySpaceCommon];
NSMutableArray *artworkImages = [NSMutableArray array];
for (AVMetadataItem *i in artworks)
{
NSString *keySpace = i.keySpace;
UIImage *im = nil;
if ([keySpace isEqualToString:AVMetadataKeySpaceID3])
{
NSDictionary *d = [i.value copyWithZone:nil];
im = [UIImage imageWithData:[d objectForKey:#"data"]];
}
else if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes])
im = [UIImage imageWithData:[i.value copyWithZone:nil]];
if (im)
[artworkImages addObject:im];
}
completion(artworkImages);
}];
}
This is just the answer of #hatfinch above but fixed and now working.
- (void)artworksForFileAtPath:(NSString *)path block:(void(^)(NSArray *artworkImages))block
{
NSURL *url = [NSURL fileURLWithPath:path];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
NSArray *keys = [NSArray arrayWithObjects:#"commonMetadata", nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
NSArray *artworks = [AVMetadataItem metadataItemsFromArray:asset.commonMetadata
withKey:AVMetadataCommonKeyArtwork
keySpace:AVMetadataKeySpaceCommon];
NSMutableArray *artworkImages = [NSMutableArray array];
for (AVMetadataItem *item in artworks) {
NSString *keySpace = item.keySpace;
if ([keySpace isEqualToString:AVMetadataKeySpaceID3]) {
NSDictionary *d = [item.value copyWithZone:nil];
[artworkImages addObject:[UIImage imageWithData:[d objectForKey:#"data"]]];
} else if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) {
[artworkImages addObject:[UIImage imageWithData:[item.value copyWithZone:nil]]];
}
}
if(block) {
block(artworkImages);
}
}];
}
If you are merely wanting to access the artwork of the music already stored on the device i.e from the iPod application, then check out this article which demonstrates how to do it using existing frameworks provided by Apple, specifically the MediaPlayer framework.
If not, it may be wise to provide a little more information about what you are trying to achieve and also provide examples of the errors you are getting when trying to compile TagLib.
EDIT:
Another solution could be to use an embedded webview and use a JavaScript library such as this one to load, parse and fetch the album artwork?
You could use this: https://github.com/EliaCereda/TagLib-ObjC
I'm try to create thumbnail image from video url.
I use AV Foundation Programming Guide.
My project have a button and an imageview. When button pressed so thumbnail image will load on uiimageview.
my code can't work,it's:
- (IBAction) btnClick : (id)sender
{
NSURL *url = [NSURL URLWithString:#"http://www.youtube.com/watch?v=bgN62D70VLk"];
AVURLAsset *myAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:myAsset];
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error = nil;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint actualTime:&actualTime error:&error];
if (halfWayImage != NULL) {
NSString *actualTimeString = (NSString *)CMTimeCopyDescription(NULL, actualTime);
NSString *requestedTimeString = (NSString *)CMTimeCopyDescription(NULL, midpoint);
NSLog(#"got halfWayImage: Asked for %#, got %#", requestedTimeString, actualTimeString);
[actualTimeString release];
[requestedTimeString release];
// Do something interesting with the image.
CGImageRelease(halfWayImage);
}
UIImage *image = [UIImage imageWithCGImage:halfWayImage];
[imageView setImage:image];
[imageGenerator release];
}
Help me for this problem,please!
Thanks!
MPMoviePlayerController has some method to handle this -
thumbnailImageAtTime:timeOption:
requestThumbnailImagesAtTimes: timeOption:
cancelAllThumbnailImageRequests:
I see a few problems in your code:
http://www.youtube.com/watch?v=bgN62D70VLk is the URL of a web page, but AVAssets must be video or audio files. Note: YouTube does not advertise the URLs of its video files.
Your call to [myAsset duration] will block. You should instead use the AVAsynchronousKeyValueLoading protocol (see loadValuesAsynchronouslyForKeys).
You are using halfwayImage after releasing it.
I would recommend watching the AVFoundation sessions from WWDC 2010, and looking at the session sample code.
This is very late but it will help some other who comes to this question.
Look into this answer for the same problem, Hope will help to other guys.