gpt4 book ai didi

ios - 音乐转换以及如何知道是否写入完成

转载 作者:可可西里 更新时间:2023-11-01 04:44:07 24 4
gpt4 key购买 nike

我必须将大文件大小的歌曲从 iTunes 库转换为较小的 8K 歌曲文件。

正如我执行异步转换一样,即使未完成写入 doc 文件夹,bool 始终返回 true。目前,我在再次调用该函数之前使用了 10 秒的延迟,它在 iPhone 5s 的过渡期间运行良好,但我想适应速度较慢的设备。

请给我一些关于我的代码的指示/建议。

-(void)startUploadSongAnalysis
{
[self updateProgressYForID3NForUpload:NO];

if ([self.uploadWorkingAray count]>=1)
{
Song *songVar = [self.uploadWorkingAray objectAtIndex:0];//core data var
NSLog(@"songVar %@",songVar.songName);
NSLog(@"songVar %@",songVar.songURL);
NSURL *songU = [NSURL URLWithString:songVar.songURL]; //URL of iTunes Lib
// self.asset = [AVAsset assetWithURL:songU];
// NSLog(@"asset %@",self.asset);
NSError *error;
NSString *subString = [[songVar.songURL componentsSeparatedByString:@"id="] lastObject];
NSString *savedPath = [self.documentsDir stringByAppendingPathComponent:[NSString stringWithFormat:@"audio%@.m4a",subString]];//save file name of converted 8kb song
NSString *subStringPath = [NSString stringWithFormat:@"audio%@.m4a",subString];

if ([self.fileManager fileExistsAtPath:savedPath] == YES)
[self.fileManager removeItemAtPath:savedPath error:&error];
NSLog(@"cacheDir %@",savedPath);

//export low bitrate song to cache
if ([self exportAudio:[AVAsset assetWithURL:songU] toFilePath:savedPath]) // HERE IS THE PROBLEM, this return true even the writing is not completed cos when i upload to my web server, it will say song file corrupted
{
// [self performSelector:@selector(sendSongForUpload:) withObject:subStringPath afterDelay:1];
[self sendRequest:2 andPath:subStringPath andSongDBItem:songVar];
}
else
{
NSLog(@"song too short, skipped");
[self.uploadWorkingAray removeObjectAtIndex:0];
[self.songNotFoundArray addObject:songVar];
[self startUploadSongAnalysis];
}
}
else //uploadWorkingAray empty
{
NSLog(@"save changes");
[[VPPCoreData sharedInstance] saveAllChanges];
}
}





#pragma mark song exporter to doc folder
- (BOOL)exportAudio:(AVAsset *)avAsset toFilePath:(NSString *)filePath
{
CMTime assetTime = [avAsset duration];
Float64 duration = CMTimeGetSeconds(assetTime);
if (duration < 40.0) return NO; // if song too short return no

// get the first audio track
NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
if ([tracks count] == 0) return NO;

NSError *readerError = nil;
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:avAsset error:&readerError];
//AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:avAsset error:&readerError]; // both works the same ?

AVAssetReaderOutput *readerOutput = [AVAssetReaderAudioMixOutput
assetReaderAudioMixOutputWithAudioTracks:avAsset.tracks
audioSettings: nil];

if (! [reader canAddOutput: readerOutput])
{
NSLog (@"can't add reader output...!");
return NO;
}
else
{
[reader addOutput:readerOutput];
}

// writer AVFileTypeCoreAudioFormat AVFileTypeAppleM4A
NSError *writerError = nil;
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:filePath]
fileType:AVFileTypeAppleM4A
error:&writerError];
//NSLog(@"writer %@",writer);
AudioChannelLayout channelLayout;
memset(&channelLayout, 0, sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

// use different values to affect the downsampling/compression
// NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
// [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
// [NSNumber numberWithFloat:16000.0], AVSampleRateKey,
// [NSNumber numberWithInt:2], AVNumberOfChannelsKey,
// [NSNumber numberWithInt:128000], AVEncoderBitRateKey,
// [NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey,
// nil];

NSDictionary *outputSettings = @{AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVEncoderBitRateKey: @(8000),
AVNumberOfChannelsKey: @(1),
AVSampleRateKey: @(8000)};

AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:outputSettings];


//\Add inputs to Write
NSParameterAssert(writerInput);
NSAssert([writer canAddInput:writerInput], @"Cannot write to this type of audio input" );

if ([writer canAddInput:writerInput])
{
[writer addInput:writerInput];
}
else
{
NSLog (@"can't add asset writer input... die!");
return NO;
}
[writerInput setExpectsMediaDataInRealTime:NO];
[writer startWriting];
[writer startSessionAtSourceTime:kCMTimeZero];
[reader startReading];

__block UInt64 convertedByteCount = 0;
__block BOOL returnValue;
__block CMSampleBufferRef nextBuffer;

dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);

[writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^{

// NSLog(@"Asset Writer ready : %d", writerInput.readyForMoreMediaData);

while (writerInput.readyForMoreMediaData)
{
nextBuffer = [readerOutput copyNextSampleBuffer];

if (nextBuffer)
{
[writerInput appendSampleBuffer: nextBuffer];
convertedByteCount += CMSampleBufferGetTotalSampleSize (nextBuffer);
//NSNumber *convertedByteCountNumber = [NSNumber numberWithLong:convertedByteCount];
//NSLog (@"writing");
CFRelease(nextBuffer);
}
else
{
[writerInput markAsFinished];

[writer finishWritingWithCompletionHandler:^{

if (AVAssetWriterStatusCompleted == writer.status)
{
NSLog(@"Writer completed");
returnValue = YES; //I NEED TO RETURN SOMETHING FROM HERE AFTER WRITING COMPLETED

dispatch_async(mediaInputQueue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
// add this to the main queue as the last item in my serial queue
// when I get to this point I know everything in my queue has been run

NSDictionary *outputFileAttributes = [[NSFileManager defaultManager]
attributesOfItemAtPath:filePath
error:nil];
NSLog (@"done. file size is %lld",
[outputFileAttributes fileSize]);
});
});
}
else if (AVAssetWriterStatusFailed == writer.status)
{
[writer cancelWriting];
[reader cancelReading];
NSLog(@"Writer failed");
return;
}
else
{
NSLog(@"Export Session Status: %d", writer.status);
}
}];
break;
}
}
}];
tracks = nil;
writer = nil;
writerInput = nil;
reader = nil;
readerOutput=nil;
mediaInputQueue = nil;
return returnValue;
//return YES;
}

最佳答案

您的方法 exportAudio:toFilePath: 实际上是一个异步 方法,需要一些修复才能成为一个合适的异步方法。

首先,您应该提供一个完成处理程序,以便向调用站点发出底层任务已完成的信号:

- (void)exportAudio:(AVAsset *)avAsset 
toFilePath:(NSString *)filePath
completion:(completion_t)completionHandler;

请注意,该方法的结果是通过完成处理程序传递的,其签名可能如下所示:

typedef void (^completion_t)(id result);

其中参数result 是方法的最终结果。当在方法中设置各种对象时,当任何出错时,您应该总是返回一个NSError对象——即使,该方法可能返回一个立即结果指示错误。

接下来,如果您查看文档,您可以阅读:

requestMediaDataWhenReadyOnQueue:usingBlock:

- (void)requestMediaDataWhenReadyOnQueue:(dispatch_queue_t)queue 
usingBlock:(void (^)(void))block

Discussion

The block should append media data to the input either until the input’s readyForMoreMediaData property becomes NO or until there is no more media data to supply (at which point it may choose to mark the input as finished using markAsFinished). The block should then exit. After the block exits, if the input has not been marked as finished, once the input has processed the media data it has received and becomes ready for more media data again, it will invoke the block again in order to obtain more.

您现在应该非常确定您的任务真正完成了。您在 传递给方法 requestMediaDataWhenReadyOnQueue:usingBlock: 的 block 中确定这一点。

当任务完成时,您调用中提供的完成处理程序completionHandler方法 exportAudio:toFilePath:completion:

当然,您需要修复您的实现,例如方法以

结尾
    tracks = nil;
writer = nil;
writerInput = nil;
reader = nil;
readerOutput=nil;
mediaInputQueue = nil;
return returnValue;
//return YES;
}

当然没有意义。清理并返回结果应在异步任务实际完成时完成。除非在设置期间发生错误,否则您需要在传递给方法 requestMediaDataWhenReadyOnQueue:usingBlock: 的 block 中确定这一点。

在任何情况下,为了将结果发送给调用站点调用完成处理程序 completionHandler 并传递一个结果对象,例如如果它成功保存它的 URL,否则一个 NSError 对象。

现在,由于我们的方法 startUploadSongAnalysis 正在调用异步方法,因此该方法也不可避免地变为异步方法!

如果我对您的原始代码的理解正确,那么您正在递归 调用它以处理大量 Assets 。为了正确实现这一点,您需要进行如下所示的一些修复。生成的“构造”不是递归方法,而是迭代调用异步方法(“异步循环”)。

您可以提供也可以不提供完成处理程序 - 同上。这取决于你 - 但我会推荐它,知道何时处理完所有 Assets 不会有什么坏处。它可能如下所示:

-(void)startUploadSongAnalysisWithCompletion:(completion_t)completionHandler
{
[self updateProgressYForID3NForUpload:NO];

// *** check for break condition: ***
if ([self.uploadWorkingAray count]>=1)
{
... stuff

//export low bitrate song to cache
[self exportAudio:[AVAsset assetWithURL:songU]
toFilePath:savedPath
completion:^(id urlOrError)
{
if ([urlOrError isKindOfClass[NSError class]]) {

// Error occurred:

NSLog(@"Error: %@", urlOrError);
// There are two alternatives to proceed:
// A) Ignore or remember the error and proceed with the next asset.
// In this case, it would be best to have a result array
// containing all the results. Then, invoke
// startUploadSongAnalysisWithCompletion: in order to proceed
// with the next asset.
//
// B) Stop with error.
// Don't call startUploadSongAnalysisWithCompletion: but
// instead invoke the completion handler passing it the error.

// A:
// possibly dispatch to a sync queue or the main thread!
[self.uploadWorkingAray removeObjectAtIndex:0];
[self.songNotFoundArray addObject:songVar];

// *** next song: ***
[self startUploadSongAnalysisWithCompletion:completionHandler];
}
else {
// Success:
// *** next song: ***
NSURL* url = urlOrError;
[self startUploadSongAnalysisWithCompletion:completionHandler];
}
}];
}
else //uploadWorkingAray empty
{
NSLog(@"save changes");
[[VPPCoreData sharedInstance] saveAllChanges];

// *** signal completion ***
if (completionHandler) {
completionHandler(@"OK");
}
}
}

关于ios - 音乐转换以及如何知道是否写入完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20557889/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com