I'm using AVAssetExportSession exportAsynchronouslyWithCompletionHandler to export an audio, but error.
(In many audios, just one audio will occur this problem.)
+ (void)exportAudioWithAsset:(AVAsset *)asset exportPath:(nonnull NSString *)exportPath completion:(nonnull void (^)(BOOL, NSError * _Nullable, NSURL * _Nullable))completion {
if (!asset || !exportPath) {
if (completion) {
completion(NO, nil, nil);
}
return;
}
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetAppleM4A];
session.outputURL = [NSURL fileURLWithPath:exportPath];
session.outputFileType = AVFileTypeAppleM4A;
session.shouldOptimizeForNetworkUse = YES;
[session exportAsynchronouslyWithCompletionHandler:^{
if (session.status == AVAssetExportSessionStatusCompleted) {
NSData *data = [NSData dataWithContentsOfFile:exportPath];
if ([data length] > 0) {
if (completion) {
completion(YES, nil, [NSURL fileURLWithPath:exportPath]);
}
} else {
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
if (completion) {
completion(NO, session.error, nil);
}
}
} else {
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
if (completion) {
completion(NO, session.error, nil);
}
}
}];
}
Session's error : Error Domain=AVFoundationErrorDomain Code=-11800 "这项操作无法完成" UserInfo={NSLocalizedFailureReason=发生未知错误(-12769), NSLocalizedDescription=这项操作无法完成, NSUnderlyingError=0x283a97630 {Error Domain=NSOSStatusErrorDomain Code=-12769 "(null)"}}
(这项操作无法完成 means couldn't complete)
To solve this problem, I replace AVAssetExportSession with AVAssetReader And AVAssertWriter to export.
Related
This question already has an answer here:
How to send data from Iphone to Apple Watch in OS2 in Objective-C
(1 answer)
Closed 7 years ago.
I am having an issue with getting an audio file that I record on my iPhone to play out of my Watch speakers.
Here is the code I have for sending from iPhone to Watch.
- (void)sendLiveAudioRecordingToWatch
{
NSError *error;
NSFileManager *files = [NSFileManager defaultManager];
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *record = [docs stringByAppendingPathComponent: #"LiveAudio.m4a"];
NSURL *URL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: #"group.myCompany.myApp"];
NSURL *outputURL = [URL URLByAppendingPathComponent: #"PhoneToWatch.mp4"];
NSString *path = [NSString stringWithFormat: #"%#", outputURL];
NSLog(#"%#", [NSURL fileURLWithPath: path]);
[files removeItemAtPath: [outputURL path] error: &error];
BOOL fileExists = [files isReadableFileAtPath: record];
if (fileExists)
{
NSLog(#"Live Audio Recording Saved!");
[files copyItemAtPath: record toPath: [outputURL path] error: &error];
if (!error)
{
NSLog(#"Temporary Recording Copied To Group");
BOOL deleteRecord = [files removeItemAtPath: record error: &error];
if (deleteRecord)
{
NSLog(#"Temporary Recording Removed");
if ([WCSession isSupported])
{
WCSession *session = [WCSession defaultSession];
NSLog(#"WCSession isSupported");
[session setDelegate: self];
[session activateSession];
if ([session isReachable])
{
NSLog(#"File Is Being Transferred");
[[WCSession defaultSession] transferFile: outputURL metadata: nil];
}
else
{
NSLog(#"Watch Not Reachable");
}
}
}
else
{
NSLog(#"Failed To Delete Temorary File");
}
}
else
{
NSLog(#"Error Copying Live Audio To Group");
}
}
else
{
NSLog(#"Error Locating Temporary Audio Location");
}
}
Here is the code I have for receiving on Watch from iPhone.
- (void)session: (WCSession *)session didReceiveFile: (WCSessionFile *)file
{
WKAudioFileAsset *asset = [WKAudioFileAsset assetWithURL: [file fileURL]];
WKAudioFilePlayerItem *playerItem = [WKAudioFilePlayerItem playerItemWithAsset: asset];
WKAudioFilePlayer *player = [WKAudioFilePlayer playerWithPlayerItem: playerItem];
[player play];
}
iOS 9.2 watchOS 2.0
Has anyone experienced this, or was able to get this working?
I got this working. WKAudioFilePlayer is used for playing through a bluetooth device, not the speakers of the Apple Watch. In WatchOS 2.0 and higher, you can use:
[self presentMediaPlayerControllerWithURL:url options:options completion:^(BOOL didPlayToEnd, NSTimeInterval endTime, NSError * __nullable error){}];
I'm using the following code:
- (AVAssetExportSession *)testRecording
{
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
NSURL *url = [[NSBundle mainBundle] URLForResource:kTestSongName withExtension:#"mp3"];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
CMTimeRange range = CMTimeRangeMake(CMTimeMake(0, 600), asset.duration);
NSError *editError;
BOOL result = [composition insertTimeRange:range ofAsset:asset atTime:kCMTimeZero error:&editError];
if (!result) {
NSLog(#"Error inserting timerange");
}
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
return exportSession;
}
This does what it's supposed to on iOS8 but on iOS7 exportSession is always nil at the end of the method. I tried using assets with different formats (mp3, caf) but the result was the same.
If I play the composition instead of exporting it the sound is fine:
AVPlayerItem * item = [[AVPlayerItem alloc] initWithAsset:composition];
AVPlayer * player = [AVPlayer playerWithPlayerItem:item];
[player play];
Any idea about what might be wrong?
exportSession = [[AVAssetExportSession alloc]
initWithAsset:songAsset
presetName:AVAssetExportPresetAppleM4A];
exportSession.outputFileType = #"com.apple.m4a-audio";
filePath = [[docDir stringByAppendingPathComponent:fileName] stringByAppendingPathExtension:#"m4a.mov"]
exportSession.outputURL = [NSURL fileURLWithPath:filePath];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSString *newPath = [NSString stringWithFormat:#"%#/%#", documentDirectory, [[[filePath lastPathComponent] substringToIndex:[[filePath lastPathComponent] length]-5] stringByAppendingString:#"a"]];
[[NSFileManager defaultManager] removeItemAtPath:newPath error:nil];
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtPath:filePath toPath:newPath error:&error];
}
if (exportSession.status == AVAssetExportSessionStatusCancelled) {
}
if (exportSession.status == AVAssetExportSessionStatusExporting) {
}
if (exportSession.status == AVAssetExportSessionStatusFailed) {
NSLog(#"erros:%#",exportSession.error);
}
if (exportSession.status == AVAssetExportSessionStatusUnknown) {
}
if (exportSession.status == AVAssetExportSessionStatusWaiting) {
}
}];
If you want to save in mp3 format, use ffmpeg.
I'm developing an iOS app, which needs to convert a video downloaded using you tube APIs into .mov format. I've tried using AVExportSession output file type set to .mov.`
NSURL * mediaURL = [NSURL fileURLWithPath:outputURL];
AVAsset *video = [AVAsset assetWithURL:mediaURL];
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPreset1920x1080];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
documentsPath=[documentsPath stringByAppendingString:#"/Youtube"];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:documentsPath isDirectory:&isDir]) {
NSError *createDirError;
if (![[NSFileManager defaultManager] createDirectoryAtPath:documentsPath withIntermediateDirectories:YES attributes:nil error:&createDirError]) {
if (createDirError) {
NSLog(#"eror while creating dir == %#", createDirError);
}
}
}
documentsPath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%d.mov",arc4random()%10000]];
exportSession.outputURL = [NSURL fileURLWithPath:documentsPath];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusCompleted:
{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:[NSURL fileURLWithPath:outputURL] completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog(#"error>%#",error.description);
}
else{
if([[NSFileManager defaultManager] fileExistsAtPath:outputURL])
{
[[NSFileManager defaultManager]removeItemAtPath:outputURL error:nil];
}
NSLog(#"asseturl>%#",assetURL);
[[NSFileManager defaultManager] removeItemAtPath:outputURL error:nil];
[self addAssetURL:assetURL toAlbum:#"Fame" withCompletionBlock:^(NSError *error) {
if(error)
{
NSLog(#"Error in saving to Fame album : %#",error.description);
return;
}
}];
}
}];
}
break;
case AVAssetExportSessionStatusFailed:
NSLog(#"Error : %#",exportSession.error.description);
default:
break;
}
NSLog(#"done processing video!");
}];
`
This code saves the video to gallery, but when I fetch the video using asset library, I get mp4 in the url.
float vocalStartMarker = 1.0;
float vocalEndMarker = 3.0;
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSURL *audioFileInput =[NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/idea_honey_bunny.mp3", [[NSBundle mainBundle] resourcePath]]];
NSURL *audioFileOutput =[NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/idea_honey_bunny.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
if (!audioFileInput || !audioFileOutput)
{
return NO;
}
[[NSFileManager defaultManager] removeItemAtURL:audioFileOutput error:NULL];
AVAsset *asset = [AVAsset assetWithURL:audioFileInput];
AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:asset
presetName:AVAssetExportPresetAppleM4A];
if (exportSession == nil)
{
return NO;
}
CMTime startTime = CMTimeMake((int)(floor(vocalStartMarker * 1)), 1);
CMTime stopTime = CMTimeMake((int)(ceil(vocalEndMarker * 1)), 1);
CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime, stopTime);
exportSession.outputURL = audioFileOutput;
exportSession.outputFileType = AVFileTypeAppleM4A;
exportSession.timeRange = exportTimeRange;
[exportSession exportAsynchronouslyWithCompletionHandler:^
{
if (AVAssetExportSessionStatusCompleted == exportSession.status)
{
NSLog(#"It worked!");
}
else if (AVAssetExportSessionStatusFailed == exportSession.status)
{
// It failed...
}
}];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileOutput error:&error];
// audioPlayer.numberOfLoops = -1;
if (audioPlayer == nil)
NSLog(#"%#",error);
else
[audioPlayer play];
return YES;
}
i want to try trimming audio file but compiler not entered in this if (AVAssetExportSessionStatusCompleted == exportSession.status)
{
NSLog(#"It worked!");
}
else if (AVAssetExportSessionStatusFailed == exportSession.status)
{
// It failed...
}
}]; bloock and file not trim
You have given same path for audioFileOutput and audioFileInput...So it is going into fail case.And also make sure that file is existed at input path...please change the OutputFile path and check..
exportAsynchronouslyWithCompletionHandler method takes some time to export new asset. So you create your audio player with asset, which not ready.
Try to change your code like this
[exportSession exportAsynchronouslyWithCompletionHandler:^
{
if (AVAssetExportSessionStatusCompleted == exportSession.status)
{
NSLog(#"It worked!");
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileOutput error:&error];
if (audioPlayer == nil) {
NSLog(#"%#",error);
}
else {
[audioPlayer play];
}
}
else if (AVAssetExportSessionStatusFailed == exportSession.status)
{
// It failed...
}
}];
Also, as Murali said above, change path for output file
I have made a sample app to trim a video by getting it from camera roll. Written code is as follows:
-(IBAction)cutVideo
{
NSString *path=[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0];
path=[path stringByAppendingPathComponent:#"new.mov"];
[self splitVideo:path];
}
- (void)splitVideo:(NSString *)outputURL
{
#try
{
NSURL *fileURL=[[NSURL alloc] init];
fileURL=[NSURL fileURLWithPath:outputURL];
fileURL=[NSURL URLWithString:outputURL];
// NSString *videoBundleURL = [[NSBundle mainBundle] pathForResource:#"samp" ofType:#"mov"];
AVAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality])
{
[self trimVideo:outputURL assetObject:asset];
}
// videoBundleURL = nil;
asset = nil;
compatiblePresets = nil;
}
#catch (NSException * e)
{
NSLog(#"Exception Name:%# Reason:%#",[e name],[e reason]);
}
}
- (void)trimVideo:(NSString *)outputURL assetObject:(AVAsset *)asset
{
#try
{
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
exportSession.outputURL = [NSURL fileURLWithPath:outputURL];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(startTime, 1);
CMTime duration = CMTimeMakeWithSeconds((endTime - startTime), 1);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
if ([[NSFileManager defaultManager] fileExistsAtPath:outputURL])
{
[[NSFileManager defaultManager] removeItemAtPath:outputURL error:nil];
}
[exportSession exportAsynchronouslyWithCompletionHandler: ^(void) {
NSLog(#"Export Status %d %#", exportSession.status, [exportSession.error description]);
}];
exportSession = nil;
}
#catch (NSException * e)
{
NSLog(#"Exception Name:%# Reason:%#",[e name],[e reason]);
}
}
all is working fine but error is in export file .. error is as follows
2012-12-12 13:27:27.896 RecordVideo[1472:907] Export Status 4 Error
Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo=0x1e577ad0
{NSErrorFailingURLStringKey=/var/mobile/Applications/A38CC8B9-A8CB-4A65-8308-
24A9BEB27626/Library/Documentation/new.mov,
NSErrorFailingURLKey=/var/mobile/Applications/A38CC8B9-A8CB-4A65-8308-
24A9BEB27626/Library/Documentation/new.mov, NSLocalizedDescription=unknown error,
NSUnderlyingError=0x1e537da0 "The operation couldn’t be completed. (OSStatus error
-12935.)", NSURL=/var/mobile/Applications/A38CC8B9-A8CB-4A65-8308-
24A9BEB27626/Library/Documentation/new.mov}
Any help will be appreciated, thanks