Iphone 从iOS设备连续录制并发送/流式传输声音到服务器

Iphone 从iOS设备连续录制并发送/流式传输声音到服务器,iphone,ios,cocoa-touch,ipad,audio-recording,Iphone,Ios,Cocoa Touch,Ipad,Audio Recording,我正在开发一款iOS应用程序,上面有一个带麦克风的按钮(以及其他功能)。当用户按下麦克风时,它会突出显示,应用程序现在应该开始从设备的麦克风录制声音并发送到服务器(一个专用于应用程序的服务器,由我认识的人开发,因此我可以影响其设计) 我正在寻找最简单但最坚固的方法来实现这一点,也就是说,我不需要开发复杂的流媒体解决方案或VoIP功能,除非它与其他任何事情一样简单 主要问题是,我们不知道用户将录制多长时间的声音,但我们希望确保声音连续发送到服务器,我们不希望等到用户完成录制。如果数据以块的形式到达

我正在开发一款iOS应用程序,上面有一个带麦克风的按钮(以及其他功能)。当用户按下麦克风时,它会突出显示,应用程序现在应该开始从设备的麦克风录制声音并发送到服务器(一个专用于应用程序的服务器,由我认识的人开发,因此我可以影响其设计)

我正在寻找最简单但最坚固的方法来实现这一点,也就是说,我不需要开发复杂的流媒体解决方案或VoIP功能,除非它与其他任何事情一样简单

主要问题是,我们不知道用户将录制多长时间的声音,但我们希望确保声音连续发送到服务器,我们不希望等到用户完成录制。如果数据以块的形式到达服务器是可以的,但是我们不希望错过用户可能正在记录的任何信息,因此一个块必须在前一个块结束的地方继续,依此类推

我们的第一个想法是创建“块”的声音剪辑,例如10秒,并将它们连续发送到服务器。有没有比我错过的更好/更简单的流媒体解决方案

我的问题是,在iOS上解决此任务最简单但仍然可靠的方法是什么


有没有一种方法可以从AVAudioRecorder正在运行的录音中提取声音块,而不必实际停止录音?

使用固定大小的声音块,而不是固定时间,实际上可能更容易。然后您可以有两个缓冲区,其中一个缓冲区当前由示例数据填充。当活动缓冲区已满时,切换到填充另一个缓冲区,同时向接收第一个缓冲区并将其发送到服务器的发送方线程发送信号

当然,您可以使用固定时间,但是您需要确保缓冲区足够大,可以保存所有样本,或者使用动态缓冲区,在需要时可以增加大小。不过,双缓冲和发送线程仍然可以是相同的。

看看
在本教程中,录制的声音将保存在soundFileURL中,然后您只需使用该内容创建nsdata,然后将其发送到服务器。
希望这有帮助

编辑:
我刚刚创建了一个包含3个按钮的版本,REC、SEND和Stop:
REC:将开始录制到文件中。
发送:将在NSData中保存该文件上记录的内容,并将其发送到服务器,然后重新开始记录。
和停止:将停止录制
代码如下: 在.h文件中:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController : UIViewController <AVAudioRecorderDelegate>

@property (nonatomic, retain) AVAudioRecorder *audioRecorder;
@property (nonatomic, retain) IBOutlet UIButton *recordButton;
@property (nonatomic, retain) IBOutlet UIButton *stopButton;
@property (nonatomic, retain) IBOutlet UIButton *sendButton;

@property BOOL stoped;

- (IBAction)startRec:(id)sender;
- (IBAction)sendToServer:(id)sender;
- (IBAction)stop:(id)sender;
@end

祝你好运。

好主意,我一定会看看这个!非常感谢。这将非常有帮助。我面临的主要问题是如何在保持录音运行的同时发送声音。有什么建议吗?我是否可以在录制文件的同时将文件发送到服务器,或者在没有任何间隙的情况下开始录制新文件(按照Joachim的建议切换缓冲区),以便将旧文件发送到服务器?根据,您可以在初始化录音机时指定要录制声音的文件位置,这意味着您必须等待文件上载到服务器,或者使用新的文件位置重新初始化您的录音机。是否可以同时运行多个录音?这样我就可以在停止旧的录音之前开始一个新的录音,以确保没有信息丢失?那么为什么不安装多个AVA录音机呢?这些东西并不总是同时工作的,我希望如果您尝试过,您可以验证它是否工作。我将尝试使用两个实例,并在它们之间进行交换。任何人只要能就如何记录和发送数据提供一些实现信息(如简短的代码示例或要使用的类示例),我就必须制作一个类似的应用程序。我必须在录音时发送数据。你找到解决办法了吗?嗨,杰克,你能解决你的问题吗?我也在寻找同样的功能。我使用了下面接受的解决方案,但结果不是很好,因为在录音之间的“间隙”中缺少了一些部分。不幸的是,在那之后我们再也没有完成这个项目,所以这取决于你是否继续工作,如果你发现了什么,我相信如果你把它贴在这里,我会非常感激:)你能分享你的源代码吗,这将节省我的努力,并且只关注现有的问题。
#import "ViewController.h"

@implementation ViewController
@synthesize audioRecorder;
@synthesize recordButton,sendButton,stopButton;
@synthesize stoped;

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
sendButton.enabled = NO;
stopButton.enabled = NO;
stoped = YES;

NSArray *dirPaths;
NSString *docsDir;

dirPaths = NSSearchPathForDirectoriesInDomains(
                                               NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir
                           stringByAppendingPathComponent:@"tempsound.caf"];

NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];

NSDictionary *recordSettings = [NSDictionary 
                                dictionaryWithObjectsAndKeys:
                                [NSNumber numberWithInt:AVAudioQualityMin],
                                AVEncoderAudioQualityKey,
                                [NSNumber numberWithInt:16], 
                                AVEncoderBitRateKey,
                                [NSNumber numberWithInt: 2], 
                                AVNumberOfChannelsKey,
                                [NSNumber numberWithFloat:44100.0], 
                                AVSampleRateKey,
                                nil];

NSError *error = nil;

audioRecorder = [[AVAudioRecorder alloc]
                 initWithURL:soundFileURL
                 settings:recordSettings
                 error:&error];
audioRecorder.delegate = self;
if (error)
{
    NSLog(@"error: %@", [error localizedDescription]);

} else {
    [audioRecorder prepareToRecord];
}
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (BOOL) sendAudioToServer :(NSData *)data {
    NSData *d = [NSData dataWithData:data];
    //now you'll just have to send that NSData to your server

    return YES;
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
NSLog(@"stoped");
    if (!stoped) {
        NSData *data = [NSData dataWithContentsOfURL:recorder.url];
        [self sendAudioToServer:data];
        [recorder record];
NSLog(@"stoped sent and restarted");
    }
}
- (IBAction)startRec:(id)sender {
if (!audioRecorder.recording)
{
    sendButton.enabled = YES;
    stopButton.enabled = YES;
    [audioRecorder record];
}
}

- (IBAction)sendToServer:(id)sender {
stoped = NO;
[audioRecorder stop];
}

- (IBAction)stop:(id)sender {
stopButton.enabled = NO;
sendButton.enabled = NO;
recordButton.enabled = YES;

stoped = YES;
if (audioRecorder.recording)
{
    [audioRecorder stop];
}
}
@end