【问题标题】:Is there a way to record an audio stream using Matt Gallagher's audio streamer?有没有办法使用 Matt Gallagher 的音频流媒体录制音频流?
【发布时间】:2011-05-30 20:01:11
【问题描述】:

我将Matt Gallagher's audio streamer 用于流式广播电台。但是如何录制音频?有没有办法将下载的数据包放入 NSData 并将其保存在 iPhone 上文档文件夹中的音频文件中?

谢谢

【问题讨论】:

  • 您打算将流保存到本地磁盘?但问题标题似乎正在录制音频。
  • 是的,“录制音频流”是指将流保存到本地磁盘。 (以我的 iPhone 为例)你有解决方案吗?
  • @FFraenz 你搞定了吗?我也需要类似的东西....
  • 不,直到现在我都不知道..
  • @FFraenz 如果你有解决方案,请与我分享......如果我得到......

标签: ios4 core-audio audio-streaming audio-recording


【解决方案1】:

是的,有,我已经做到了。我的问题是能够在同一个流媒体中播放它(在别处询问)。它将使用 iOS 中的标准 AVAudioPlayer 播放。但是,这会通过将数据写入流媒体代码中来将数据保存到文件中。

这个例子缺少一些错误检查,但会给你主要的想法。

首先,从主线程调用开始和停止录制。当有人按下记录时,这是在我的 viewController 中:

//---------------------------------------------------------
// Record button was pressed (toggle on/off)
// writes a file to the documents directory using date and time for the name
//---------------------------------------------------------

-(IBAction)recordButton:(id)sender {
    // only start if the streamer is playing (self.streamer is my streamer instance)
    if ([self.streamer isPlaying])     {

    NSDate *currentDateTime = [NSDate date];      // get current date and time                                         
    NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];      
    [dateFormatter setDateFormat:@"EEEE MMMM d YYYY 'at' HH:mm:ss"];
    NSString *dateString = [dateFormatter stringFromDate:currentDateTime];

    self.isRecording = !self.isRecording;       // toggle recording state BOOL
    if (self.isRecording)
    {
        // start recording here
        // change the record button to show it is recording - this is an IBOutlet
        [self.recordButtonImage setImage:[UIImage imageNamed:@"Record2.png"] forState:0];
       // call AudioStreamer to start recording. It returns the file pointer back
       // 
       self.recordFilePath = [self.streamer recordStream:TRUE fileName:dateString];    // start file stream and get file pointer              
    } else
    {
        //stop recording here
        // change the button back
        [self.recordButtonImage setImage:[UIImage imageNamed:@"Record.png"] forState:0];
        // call streamer code, stop the recording. Also returns the file path again.
        self.recordFilePath = [self.streamer recordStream:FALSE fileName:nil];     // stop stream and get file pointer             
        // add to "recorded files" for selecting a recorderd file later.
        // first, add channel, date, time
        dateString = [NSString stringWithFormat:@"%@ Recorded on %@",self.model.stationName, dateString];  // used to identify the item in a list laster
        // the dictionary will be used to hold the data on this recording for display elsewhere
        NSDictionary *row1 = [[[NSDictionary alloc] initWithObjectsAndKeys: self.recordFilePath, @"path", dateString, @"dateTime", nil] autorelease];
        // save the stream info in an array of recorded Streams
        if (self.model.recordedStreamsArray == nil) {
            self.model.recordedStreamsArray = [[NSMutableArray alloc] init]// init the array
        }
        [self.model.recordedStreamsArray addObject:row1];          // dict for this recording
        }
    }
}

现在,在 AudioStreamer.m 中,我需要处理上面的记录设置调用

- (NSString*)recordStream:(BOOL)record fileName:(NSString *)fileName
{
    // this will start/stop recording, and return the file pointer
    if (record) {
        if (state == AS_PLAYING)
        {
            // now open a file to save the data into
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,       NSUserDomainMask, YES);
            NSString *documentsDirectory = [paths objectAtIndex:0];
            // will call this an mp3 file for now (this may need to change)
            NSMutableString *temp = [NSMutableString stringWithString:[documentsDirectory stringByAppendingFormat:@"/%@.mp3",fileName]];
            // remove the ':' in the time string, and create a file name w/ time & date
           [temp replaceOccurrencesOfString:@":" withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];

           self.filePath = temp;              // file name is date time generated.
            NSLog(@"Stream Save File Open = %@", self.filePath);
            // open the recording file stream output
            self.fileStream = [NSOutputStream outputStreamToFileAtPath:self.filePath append:NO];
           [self.fileStream open];
           NSLog(@"recording to %@", self.fileStream);
           self.isRecording = TRUE;
           return (self.filePath);             // if started, send back the file path
        }
      return (nil);                           // if not started, return nil for error checking
} else {
    // save the stream here to a file.
    // we are done, close the stream.
        if (self.fileStream != nil) {
            [self.fileStream close];
            self.fileStream = nil;
        }
        NSLog(@"stop recording");
        self.isRecording = FALSE;
        return (self.filePath);                       // when stopping, return nil
    }
}

最后,我们需要修改流媒体的数据部分以实际保存字节。需要修改方法中的流代码:-(void)handleReadFromStream:(CFReadStreamRef)aStreameventType:(CFStreamEventType)eventType 在该方法中向下滚动,直到找到:

@synchronized(self)
{
    if ([self isFinishing] || !CFReadStreamHasBytesAvailable(stream))
    {
        return;
    }

    //
    // Read the bytes from the stream
    //
    length = CFReadStreamRead(stream, bytes, kAQDefaultBufSize);

    if (length == -1)
    {
        [self failWithErrorCode:AS_AUDIO_DATA_NOT_FOUND];
        return;
    }

在length =行之后,添加如下代码:

            //
            // if recording, save the raw data to a file
            //
            if(self.isRecording && length != 0){
                //
                // write the data to a file
                //
                NSInteger       bytesWritten;
                NSInteger       bytesWrittenSoFar;
                bytesWrittenSoFar = 0;
                do {
                    bytesWritten = [self.fileStream write:&bytes[bytesWrittenSoFar] maxLength:length - bytesWrittenSoFar];
                    NSLog(@"bytesWritten = %i",bytesWritten);
                    if (bytesWritten == -1) {
                        [self.fileStream close];
                        self.fileStream = nil;
                        NSLog(@"File write error");
                        break;
                    } else {
                        bytesWrittenSoFar += bytesWritten;
                    }
                } while (bytesWrittenSoFar != length);
            }

以下是 .h 声明:

添加到 AudioStreamer.h 的接口

// for recording and saving a stream
NSString*        filePath;
NSOutputStream*  fileStream;
BOOL isRecording;
BOOL isPlayingFile;

在您的视图控制器中,您将需要:

@property(nonatomic, assign) IBOutlet UIButton* recordButtonImage;
@property(nonatomic, assign) BOOL isRecording;
@property (nonatomic, copy)   NSString* recordFilePath;

希望这对某人有所帮助。如果有问题请告诉我,并且总是很高兴听到有人可以改进这一点。

另外,有人问起 self.model.xxx 模型是我创建的一个数据对象,让我可以轻松地传递多个对象使用的数据,也可以被多个对象修改。我知道,全局数据的形式不好,但有时它只是让它更容易访问。我在调用时将数据模型传递给每个新对象。我在模型中保存了一组频道、歌曲名称、艺术家姓名和其他与流相关的数据。我还将我想通过启动保留的任何数据放在这里,例如设置,并在每次更改持久数据时将此数据模型写入文件。在此示例中,您可以将数据保存在本地。如果您在模型传递方面需要帮助,请告诉我。

【讨论】:

  • 所以现在我真正的问题是如何在同一个程序中播放这段录音。我尝试使用 URL 文件调用打开流,但没有任何乐趣。所以有人请弄清楚如何回放这些数据! :-)
  • 模型只是我拥有的一个对象,用于由多个对象共享的“全局”数据(保持火焰:-)。所以我有一个名为“model”的数据模型对象,它包含我的 url 字符串、我听过的电台数组以及我共享的其他数据,比如艺术家姓名和歌曲标题,我将它从一个对象传递到另一个对象以获得数据不止一处需要。可能不是最好的方法,但它确实有效。
  • mp3文件生成,但不能播放。你试过播放那个文件吗?
  • 如果你玩成功了,请告诉我。谢谢
  • 文件未使用 AVAudioPlayer 加载。我猜数据没有正确写入。请多指导。 AudioPlayer 未正确加载:Error Domain=NSOSStatusErrorDomain Code=1685348671 “操作无法完成。(OSStatus 错误 1685348671。)”
【解决方案2】:

好的,这就是我播放录制文件的方式。播放文件时,站 URL 包含文件的路径。 self.model.playRecordedSong 包含我想要播放的流中的秒数的时间值。我保留了歌曲名称和时间索引的字典,因此我可以在任何歌曲的开头跳入录制的流。使用 0 从头开始​​。

  NSError *error;
    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:self.model.stationURL, [[NSBundle mainBundle] resourcePath]]];
    // get the file URL  and then create an audio player if we don't already have one.
    if (audioPlayer == nil) {
        // set the seconds count to the proper start point (0, or some time into the stream)
        // this will be 0 for start of stream, or some value passed back if they picked a song.
        self.recordPlaySecondsCount = self.model.playRecordedSong;
        //create a new player
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
        // set self so we can catch the end of file.
        [audioPlayer setDelegate: self];
        // audio player needs an NSTimeInterval. Get it from the seconds start point.
        NSTimeInterval interval = self.model.playRecordedSong;
        // seek to the proper place in file.
        audioPlayer.currentTime = interval;
    }
    audioPlayer.numberOfLoops = 0;          // do not repeat

    if (audioPlayer == nil)
        NSLog(@"AVAudiolayer error: %@", error);
    // I need to do more on the error of no player
    else {
        [audioPlayer play];
    }

我希望这可以帮助您播放录制的文件。

【讨论】:

  • 需要注意的一点:许多流包含歌曲和艺术家的元数据,或其他信息。如果您也记录该数据,则在回放时,您会在该数据经过时听到常规的“滴答声”。如果您在播放时听到这种噪音,那就是原因。
【解决方案3】:

试试这个课程这有完整的解决方案所有广播流录音正在播放..

In Git Hub You Can Find This Use This Class Very Easy To Use

【讨论】:

  • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
  • 我正在谈论我想提供该类 zip 的 streming 类,所以添加此链接以直接下载
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多