Exporting Multiple Tracks: AVMutableComposition and AVAssetExportSession - ios

I have 4 videos files which I have edited and added to a mutable composition. I am trying to use the export session to export the files, however, when I am exporting, only the first track in the list of tracks below gets exported
<AVAssetExportSession: 0x60800001da50, asset = <AVMutableComposition: 0x6080002240a0 tracks = (
"<AVMutableCompositionTrack: 0x600000224ca0 trackID = 1, mediaType = vide, editCount = 8>",
"<AVMutableCompositionTrack: 0x600000226da0 trackID = 2, mediaType = vide, editCount = 10>",
"<AVMutableCompositionTrack: 0x60000023e180 trackID = 3, mediaType = vide, editCount = 3>",
"<AVMutableCompositionTrack: 0x60000023e500 trackID = 4, mediaType = vide, editCount = 7>"
)>, presetName = AVAssetExportPreset1280x720, outputFileType = (null)
Only the first track with trackID = 1 gets exported. Here is the export session source:
// Create path to output file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:#"ProcessedVideo-%d.mov", arc4random() % 1000]];
NSURL *url = [NSURL fileURLWithPath:myPathDocs];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:batchComposition presetName:AVAssetExportPreset1280x720];
NSLog(#"%#", exporter);
exporter.outputURL = url;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
[exporter exportAsynchronouslyWithCompletionHandler:^(void) {
switch (exporter.status) {
case AVAssetExportSessionStatusCompleted:
NSLog(#"Completed");
break;
case AVAssetExportSessionStatusFailed:
NSLog(#"Failed:%#",exporter.error);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Canceled:%#",exporter.error);
break;
default:
break;
}
}];
Does anyone have any ideas how I can get all 4 tracks to export to a single .mov file using export session?

You can only export to one NSURL with AVAssetExportSession. If you want to export 4 separate files, you will have to export 4 times.

Thanks to jlw for his help. What I found the problem was that I was adding multiple video tracks to mutable composition. What instead I should have done is made a single video track and applied all edits from the other asset tracks onto the single video tracks. Seems AVAssetExportSession will only export a single track as noted by jlw.
Summary:
Create mutable composition
Create mutable composition track
Apply asset tracks to the composition (insertTimeRange)
Export the mutable composition

I think there is no need to create new AVMutableComposition. Instead just assign your AVVideoComposition? to the videoComposition property of your exporter.
guard let playerItem = getPlayerItem() as? AVPlayerItem else {
return
}
let asset = playerItem.asset
let videoComposition: AVVideoComposition? = playerItem.videoComposition
let path = NSTemporaryDirectory().stringByAppendingFormat("/video.mov")
if NSFileManager.defaultManager().fileExistsAtPath(path) {
do {
try NSFileManager.defaultManager().removeItemAtPath(path)
} catch {
print("Temporary file removing error.")
}
}
let outputURL = NSURL.fileURLWithPath(path)
guard let exporter = AVAssetExportSession(asset: asset,
presetName: AVAssetExportPresetHighestQuality) else {
return
}
exporter.outputURL = outputURL
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.shouldOptimizeForNetworkUse = true
exporter.videoComposition = videoComposition // 👈
exporter.exportAsynchronouslyWithCompletionHandler {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(outputURL)
}) { (success: Bool, error: NSError?) -> Void in
if success {
} else {
}
}
}

Related

Unable to convert to mp4 in iOS 13

On a device running iOS 13, [exportSession exportAsynchronouslyWithCompletionHandler: always fails with message "The operation could not be completed" while converting .MOV video to mp4. However, the same code runs fine on iOS prior to 13 i.e 12. I am pasting below my complete method
- (void)encodeVideo:(NSString *)videoURL
{
// Create the asset url with the video file
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:videoURL] options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
// Check if video is supported for conversion or not
if ([compatiblePresets containsObject: AVAssetExportPresetLowQuality])
{
//Create Export session
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];
//Creating temp path to save the converted video
NSString* documentsDirectory= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* myDocumentPath= [documentsDirectory stringByAppendingPathComponent:#"temp.mp4"];
NSURL *url = [[NSURL alloc] initFileURLWithPath:myDocumentPath];
//Check if the file already exists then remove the previous file
if ([[NSFileManager defaultManager]fileExistsAtPath:myDocumentPath])
{
[[NSFileManager defaultManager]removeItemAtPath:myDocumentPath error:nil];
}
exportSession.outputURL = url;
//set the output file format if you want to make it in other file format (ex .3gp)
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status])
{
case AVAssetExportSessionStatusFailed:
NSLog(#"Export session failed");
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
case AVAssetExportSessionStatusCompleted:
{
//Video conversion finished
NSLog(#"Successful!");
}
break;
default:
break;
}
}];
}
else
{
NSLog(#"Video file not supported!");
}
}
1.
Create Folder -
let filePath = documentDirectory.appendingPathComponent("FolderName")
if !fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.createDirectory(atPath: filePath.path, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error.localizedDescription)
return nil
}
}
2.
Let url = videoURL
destinationURL = filePath.appendingPathComponent("filename.mp4")
url.startAccessingSecurityScopedResource()
do {
try FileManager.default.copyItem(at: url, to: destinationURL)
} catch {
Logging.Log.error("EncodeVideo failed \(error.localizedDescription)")
}
url.startAccessingSecurityScopedResource()
Start Mov to MP4 now it is working.

Is it possible to save a music from the Ipod library into my app with swift?

I have the reference of an MPMediaItem when a user selects an audio from the iPod library. I am getting the asset URL of that item by using
let url = item.valueForProperty(MPMediaItemPropertyAssetURL)
But this is not giving me the exact physical location of the file, instead, it is giving me an URL w.r.t iPod library.
ipod-library://item/item.mp3?id=1840064795502796074
Is there a way to get the physical URL of a song from an iPod library?
EDIT - actually I want to extract NSData from the physical file and send it to my backend server, so I need the physical file URL and not the relative URL
MPmediaPickerController is working, I select the song and its playing but I don't want to play the song.I have tried to upload the audio files to a server. And I have using MPMedia Picker view in list audio, when I am going select the audio I will upload to server(HTTP), How can I do this??? How to accessing the media library with Swift code?
Adapting Krishna's answer, which uses AVAssetExportSession to save the MPMediaItem to a file, you can do something like the following in Swift 3:
/// Export MPMediaItem to temporary file.
///
/// - Parameters:
/// - assetURL: The `assetURL` of the `MPMediaItem`.
/// - completionHandler: Closure to be called when the export is done. The parameters are a boolean `success`, the `URL` of the temporary file, and an optional `Error` if there was any problem. The parameters of the closure are:
///
/// - fileURL: The `URL` of the temporary file created for the exported results.
/// - error: The `Error`, if any, of the asynchronous export process.
func export(_ assetURL: URL, completionHandler: #escaping (_ fileURL: URL?, _ error: Error?) -> ()) {
let asset = AVURLAsset(url: assetURL)
guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
completionHandler(nil, ExportError.unableToCreateExporter)
return
}
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(NSUUID().uuidString)
.appendingPathExtension("m4a")
exporter.outputURL = fileURL
exporter.outputFileType = "com.apple.m4a-audio"
exporter.exportAsynchronously {
if exporter.status == .completed {
completionHandler(fileURL, nil)
} else {
completionHandler(nil, exporter.error)
}
}
}
func exampleUsage(with mediaItem: MPMediaItem) {
if let assetURL = mediaItem.assetURL {
export(assetURL) { fileURL, error in
guard let fileURL = fileURL, error == nil else {
print("export failed: \(error)")
return
}
// use fileURL of temporary file here
print("\(fileURL)")
}
}
}
enum ExportError: Error {
case unableToCreateExporter
}
Or, in Swift 2:
/// Export MPMediaItem to temporary file.
///
/// - Parameters:
/// - assetURL: The `assetURL` of the `MPMediaItem`.
/// - completionHandler: Closure to be called when the export is done. The parameters are a boolean `success`, the `URL` of the temporary file, and an optional `Error` if there was any problem. The parameters of the closure are:
///
/// - fileURL: The `URL` of the temporary file created for the exported results.
/// - error: The `Error`, if any, of the asynchronous export process.
func export(assetURL: NSURL, completionHandler: (NSURL?, ErrorType?) -> ()) {
let asset = AVURLAsset(URL: assetURL)
guard let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) else {
completionHandler(nil, ExportError.unableToCreateExporter)
return
}
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
.URLByAppendingPathComponent(NSUUID().UUIDString)!
.URLByAppendingPathExtension("m4a")
exporter.outputURL = fileURL
exporter.outputFileType = "com.apple.m4a-audio"
exporter.exportAsynchronouslyWithCompletionHandler {
if exporter.status == .Completed {
completionHandler(fileURL, nil)
} else {
completionHandler(nil, exporter.error)
}
}
}
func exampleUsage(with mediaItem: MPMediaItem) {
if let assetURL = mediaItem.assetURL {
export(assetURL) { fileURL, error in
guard let fileURL = fileURL where error == nil else {
print("export failed: \(error)")
return
}
// use fileURL of temporary file here
print("\(fileURL)")
}
}
}
enum ExportError: ErrorType {
case unableToCreateExporter
}
As you can see, I put it in a temporary folder rather than the Documents folder. Also, I use UUID rather than the number of seconds since some reference date to generate the temporary file. But the idea is basically the same.
After selecting the song from library convert your MPMediaItem object into NSData and upload it to server using multipart form data.
Convert MPMediaItem to NSData
-( void)mediaItemToData : (MPMediaItem * ) curItem
{
NSURL *url = [curItem valueForProperty: MPMediaItemPropertyAssetURL];
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: songAsset
presetName:AVAssetExportPresetAppleM4A];
exporter.outputFileType = #"com.apple.m4a-audio";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * myDocumentsDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
[[NSDate date] timeIntervalSince1970];
NSTimeInterval seconds = [[NSDate date] timeIntervalSince1970];
NSString *intervalSeconds = [NSString stringWithFormat:#"%0.0f",seconds];
NSString * fileName = [NSString stringWithFormat:#"%#.m4a",intervalSeconds];
NSString *exportFile = [myDocumentsDirectory stringByAppendingPathComponent:fileName];
NSURL *exportURL = [NSURL fileURLWithPath:exportFile];
exporter.outputURL = exportURL;
// do the export
// (completion handler block omitted)
[exporter exportAsynchronouslyWithCompletionHandler:
^{
int exportStatus = exporter.status;
switch (exportStatus)
{
case AVAssetExportSessionStatusFailed:
{
NSError *exportError = exporter.error;
NSLog (#"AVAssetExportSessionStatusFailed: %#", exportError);
break;
}
case AVAssetExportSessionStatusCompleted:
{
NSLog (#"AVAssetExportSessionStatusCompleted");
NSData *data = [NSData dataWithContentsOfFile: [myDocumentsDirectory
stringByAppendingPathComponent:fileName]];
[arrayMusic addObject:data];
data = nil;
break;
}
case AVAssetExportSessionStatusUnknown:
{
NSLog (#"AVAssetExportSessionStatusUnknown"); break;
}
case AVAssetExportSessionStatusExporting:
{
NSLog (#"AVAssetExportSessionStatusExporting"); break;
}
case AVAssetExportSessionStatusCancelled:
{
NSLog (#"AVAssetExportSessionStatusCancelled"); break;
}
case AVAssetExportSessionStatusWaiting:
{
NSLog (#"AVAssetExportSessionStatusWaiting"); break;
}
default:
{
NSLog (#"didn't get export status"); break;
}
}
}];
}

How can I export a AVPlayer audio mp3 file using AVAssetExportSession?

I am now trying to export an mp3 file that has been player using AVPlayer (using an url) so it doesn't have to be downloaded twice.
This is my sample code:
I've tried every outputFileType...
self.exporter = [[AVAssetExportSession alloc] initWithAsset:self.asset presetName:AVAssetExportPresetPassthrough];
}
NSError *error;
NSLog(#"export.supportedFileTypes : %#",self.exporter.supportedFileTypes);
// "com.apple.quicktime-movie",
// "com.apple.m4a-audio",
// "public.mpeg-4",
// "com.apple.m4v-video",
// "public.3gpp",
// "org.3gpp.adaptive-multi-rate-audio",
// "com.microsoft.waveform-audio",
// "public.aiff-audio",
// "public.aifc-audio",
// "com.apple.coreaudio-format"
self.exporter.outputFileType = #"public.aiff-audio";
self.exporter.shouldOptimizeForNetworkUse = YES;
NSURL *a = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
NSURL *url = [a URLByAppendingPathComponent:#"filename.mp3"];
NSString *filePath = [url absoluteString];
self.exporter.outputURL = url;
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]){
[self.exporter exportAsynchronouslyWithCompletionHandler:^{
if (self.exporter.status == AVAssetExportSessionStatusCompleted)
{
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]){
NSLog(#"File doesn't exist at path");
}else {
NSLog#"File saved!");
}
}
else if(self.exporter.status == AVAssetExportSessionStatusFailed){
NSLog(#"Failed");
}else if(self.exporter.status == AVAssetExportSessionStatusUnknown){
NSLog(#"Unknown");
}else if(self.exporter.status == AVAssetExportSessionStatusCancelled){
NSLog(#"Cancelled");
}else if(self.exporter.status == AVAssetExportSessionStatusWaiting){
NSLog(#"Waiting");
}else if(self.exporter.status == AVAssetExportSessionStatusExporting){
NSLog(#"Exporting");
}
NSLog(#"Exporter error! : %#",self.exporter.error);
}];
}}else{
NSLog(#"File already exists at path");
}
If this is not possible to accomplish, is there any work around?
Also, as I can change the format of the audio file. What's the ideal type to work with AVAudioPlayer?
It appears AVAssetExportSession only supports filetypes for mp3 transcoding with com.apple.quicktime-movie (.mov) and com.apple.coreaudio-format (.caf) using the AVAssetExportPresetPassthrough preset. You must also be sure to use one of these file extensions when writing your output file otherwise it won't save.
Supported output filetype and extensions for an mp3 input file are in bold (tested on OS X 10.11.6):
com.apple.quicktime-movie (.mov)
com.apple.m4a-audio (.m4a)
public.mpeg-4 (.mp4)
com.apple.m4v-video (.m4v)
org.3gpp.adaptive-multi-rate-audio (.amr)
com.microsoft.waveform-audio (.wav)
public.aiff-audio (.aiff)
public.aifc-audio (.aifc)
com.apple.coreaudio-format (.caf)
If you don't mind performing a proper transcode of the audio data to another format, then you don't have to use the AVAssetExportPresetPassthrough preset. There are also AVAssetExportPresetLowQuality, AVAssetExportPresetMediumQuality, and AVAssetExportPresetHighestQuality. In the sample code that follows, the output URL has extension .m4a and the resulting transcode plays in iTunes and other media players:
AVAsset * asset = [AVAsset assetWithURL:inputURL];
AVAssetExportSession * exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.outputURL = outputURL;
exportSession.metadata = asset.metadata;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"AV export succeeded.");
}
else if (exportSession.status == AVAssetExportSessionStatusCancelled)
{
NSLog(#"AV export cancelled.");
}
else
{
NSLog(#"AV export failed with error: %# (%ld)", exportSession.error.localizedDescription, (long)exportSession.error.code);
}
}];
I tried to export audio file with mp3 format from ipod library. and this is my solution below.
extension DirectoryListViewController: MPMediaPickerControllerDelegate {
public func mediaPicker(_ mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {
guard let mediaItem = mediaItemCollection.items.first else { ImportExternalFileService.shared.alertImportError(); return }
guard let url = mediaItem.assetURL else { ImportExternalFileService.shared.alertImportError(); return }
guard let songTitle = mediaItem.title else { ImportExternalFileService.shared.alertImportError(); return }
guard let exportSession = AVAssetExportSession(asset: AVURLAsset(url: url), presetName: AVAssetExportPresetAppleM4A) else {
ImportExternalFileService.shared.alertImportError(); return
}
exportSession.outputFileType = .m4a
exportSession.metadata = AVURLAsset(url: url).metadata
exportSession.shouldOptimizeForNetworkUse = true
guard let fileExtension = UTTypeCopyPreferredTagWithClass(exportSession.outputFileType!.rawValue as CFString, kUTTagClassFilenameExtension) else {
ImportExternalFileService.shared.alertImportError(); return
}
let documentURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let outputURL = documentURL.appendingPathComponent("\(songTitle).\(fileExtension.takeUnretainedValue())")
/* Dont't forget to remove the existing url, or exportSession will throw error: can't save file */
do {
try FileManager.default.removeItem(at: outputURL)
} catch let error as NSError {
print(error.debugDescription)
}
exportSession.outputURL = outputURL
exportSession.exportAsynchronously(completionHandler: {
if exportSession.status == .completed {
DispatchQueue.main.async {
ImportExternalFileService.shared.importRecordFile(url: exportSession.outputURL!)
}
} else {
print("AV export failed with error:- ", exportSession.error!.localizedDescription)
}
})
}
public func mediaPickerDidCancel(_ mediaPicker: MPMediaPickerController) {
dismiss(animated: true, completion: nil)
}
}
You can't, but you can export m4a.
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:audioAsset presetName:AVAssetExportPresetAppleM4A];
exportSession.outputURL = [NSURL fileURLWithPath:exportPath];
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
}];

AVAssetExportSession Trim & Download

I'm trying to trim and download online video using AVExportSession.
code:
FileMove *fileMove = (FileMove*)data;
NSString *url = #"http://download.wavetlan.com/SVV/Media/HTTP/H264/Talkinghead_Media/H264_test1_Talkinghead_mp4_480x360.mp4";
NSURL *videoURL = [NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"VideoURL: %#",videoURL);
AVAsset *anAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
NSURL *outputURL = [NSURL fileURLWithPath:fileMove.dst];
NSLog(#"outputURL: %#",outputURL);
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
if ([[NSFileManager defaultManager] fileExistsAtPath:fileMove.dst])
[[NSFileManager defaultManager] removeItemAtPath:fileMove.dst error:nil];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [[exportSession error]description ]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
default:
break;
}
}];
}
Error:
Export failed: Error Domain=AVFoundationErrorDomain Code=-11800 "The
operation could not be completed" UserInfo=0x635a820
{NSLocalizedDescription=The operation could not be completed,
NSUnderlyingError=0x1ff4240 "The operation couldn’t be completed.
(OSStatus error -12780.)", NSLocalizedFailureReason=An unknown error
occurred (-12780)}
Do you guys see any issue in the code ? Is there any limitation in AVExportSession accessing online video ?
I've managed to trim remote video using AVFoundation. Here is the sample code which is written in Swift:
let range: CMTimeRange
let sourceURL: NSURL
let targetFileURL: NSURL
let requiredKeys = [ "exportable", "tracks" ]
let asset = AVAsset(URL: sourceURL)
asset.loadValuesAsynchronouslyForKeys(requiredKeys) {
// Error handling code here
precondition(asset.statusOfValueForKey("exportable", error: nil) == .Loaded)
precondition(asset.statusOfValueForKey("tracks", error: nil) == .Loaded)
precondition(asset.exportable)
let composition = AVMutableComposition()
do {
try composition.insertTimeRange(range, ofAsset: asset, atTime: kCMTimeZero)
} catch {
// Error handling code here
return
}
let finalComposition = composition.copy() as! AVComposition
guard let export = AVAssetExportSession(asset: finalComposition, presetName: AVAssetExportPresetPassthrough) else {
// Error handling code here
return
}
export.outputURL = targetFileURL
export.outputFileType = AVFileTypeMPEG4
export.exportAsynchronouslyWithCompletionHandler {
switch export.status {
case .Completed:
// Alright!
break
case .Cancelled, .Failed:
// Error handling code here
break
default:
fatalError("Shouldn't be called")
}
}
}
The URL used for creating an AVAsset needs to be a local file that you have access to. You will need to download it onto the device before creating the AVAsset like so
I think we are not able to export online videos
see here:
Unable to export AVPlayerItem
So I guess, you can download video first locally and then trim,save and use it.

How to add and retrieve custom meta data to a video file in iOS programming?

I am trying to save video with custom metadata relevant for my app and trying to retrieve it when the user selects that video from the library. I am not sure if I am saving the meta data right as I am not able to see anything when I try to retrieve the metadata. I am also not sure if I am retrieving the meta data correctly. I am new to iOS, any help is appreciated. I have searched many threads and developer library but could not get this to work.
I am trying to save metadata in the recordingDidFinishToOutputFileURL delegate function. Video is getting saved in the library.
NSMutableArray *metadata = [NSMutableArray array];
AVMutableMetadataItem *mi = [AVMutableMetadataItem metadataItem];
mi.key = AVMetadataCommonKeyTitle;
mi.keySpace = AVMetadataKeySpaceCommon;
mi.value = #"title";
[metadata addObject:mi];
NSLog(#"Output saving:%#",outputFileURL);
AVAsset *video = [AVAsset assetWithURL:outputFileURL];
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetPassthrough];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.metadata = metadata;
exportSession.outputURL = outputFileURL;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog(#"done processing video!");
UISaveVideoAtPathToSavedPhotosAlbum(outputFileURL.path, self, #selector(video:didFinishSavingWithError: contextInfo:), nil);
}];
I am trying to retrieve the video in didFinishPickingMediaWithInfo delegate function to check the metadata but not able to see anything in the completionhandler function
if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
video_selected = TRUE;
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSLog(#"video has %#", videoURL.path);
AVAsset *videoAsset = [AVAsset assetWithURL:videoURL];
NSLog(#"Loading metadata...");
NSArray *keys = [[NSArray alloc] initWithObjects:#"commonMetadata", nil];
NSMutableArray *metadata = [[NSMutableArray alloc] init];
[videoAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
[metadata removeAllObjects];
for (NSString *format in [videoAsset availableMetadataFormats])
{
[metadata addObjectsFromArray:[videoAsset metadataForFormat:format]];
NSLog(#"Printing metadata-%#",metadata);
}
}];
check your errors. You're exporting to where the file already exists so it's exporting into itself which won't work. Just export to a different location
These keys work fine:
AVMetadataCommonKeyAuthor
AVMetadataCommonKeyDescription
AVMetadataCommonKeyCopyrights
MP4 files support the ISO userdata keyspace and the iTunes metadata keyspace. The above keys don't have an ISO userdata mapping, so that's why they're not working. Likewise, you can't put AVMetadataQuickTimeMetadataKey* in an MP4 file because it doesn't support that keyspace.
var metadata: [AVMetadataItem] = []
let mi = AVMutableMetadataItem()
mi.identifier = AVMetadataIdentifier.commonIdentifierDescription
mi.keySpace = .common
mi.value = "Milan Description" as (NSCopying & NSObjectProtocol)?
metadata.append(mi)
let mi1 = AVMutableMetadataItem()
mi1.identifier = AVMetadataIdentifier.commonIdentifierCopyrights
mi1.keySpace = .common
mi1.value = "Milan copy" as (NSCopying & NSObjectProtocol)?
metadata.append(mi1)
print("Output saving:\(trackObj.getTrackUrl())")
let video = AVAsset(url: trackObj.getTrackUrl())
let exportSession = AVAssetExportSession(asset: video, presetName: AVAssetExportPresetPassthrough)
exportSession?.metadata = metadata
exportSession?.outputURL = volumeBoosterViewModel.audioResultRootFolderURL.appendingPathComponent(trackObj.filePathName)
exportSession?.outputFileType = AVFileType.mp3
exportSession?.exportAsynchronously(completionHandler: {[weak self]
() -> Void in
if exportSession!.status == AVAssetExportSession.Status.completed {
// All is working fine!!
print("success")
//get metadata
let asset = AVAsset(url: Your url)
if asset.commonMetadata.count > 0 {
for item in asset.metadata {
//get metadata
print(item.value)
}
}
}
})

Resources