7
votes

I have a view controller that uses AVPlayer. this view controller can load a modal view controler where the user can record audio using AVAudioRecorder.

this is what happens:

if the user plays the composition in the fist controller with [AVPLayer play] the AVAudioRecorder will not record in the modal view controller. there are no errors but the current time returned by AVAudioRecorder is 0.0;

if the user dismisses the modal dialog and reloads it. AVAudioRecorder works fine.

this can be repeated over and over AVAudioRecorder does not work the first time it is invoked after a [AVPlayer play] call

I have been fighting with this for days and just have reorganized my code related to both AVPlayer and AVAudioRecorder and it's still acting weird.

Any help or pointer is much appreciated

Thanks in advance

Jean-Pierre

7
hi try it for solution it worked for me stackoverflow.com/questions/4148123/…user1172555

7 Answers

9
votes

I've been having the same problem too, and this is the solution I found.

When recording, write this line code after AVAudioRecord is initialized:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:NULL];

Then invoke record method.

3
votes

How are you setting the AVAudioSession's category, i.e., play or record? Try something like

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
1
votes

I've been having the same problem. I use AVPlayer to play compositions (previous recordings I've used AVAudioRecord for). However, I found that once I've used AVPlayer I could no longer use AVAudioRecorder. After some searching, I discovered that so long as AVPlayer is instantiated in memory and has been played at least once (which is usually what you do immediately after instantiating it) AVAudioRecorder will not record. However, once AVPlayer is dealloc'd, AVAudioRecorder is then free to record again. It appears that AVPlayer holds on to some kind of connection that AVAudioRecorder needs, and it's greedy...it won't let it go until you pry it from it's cold dead hands.

This is the solution I've found. Some people claim that instantiating AVPlayer takes too much time to keep breaking down and setting back up. However, this is not true. Instantiating AVPlayer is actually quite trivial. So also is instantiating AVPlayerItem. What isn't trivial is loading up AVAsset (or any of it's subclasses). You really only want to do that once. They key is to use this sequence:

  1. Load up AVAsset (for example, if you're loading from a file, use AVURLAsset directly or add it to a AVMutableComposition and use that) and keep a reference to it. Don't let it go until you're done with it. Loading it is what takes all the time.
  2. Once you're ready to play: instantiate AVPlayerItem with your asset, then AVPlayer with the AVPlayerItem and play it. Don't keep a reference to AVPlayerItem, AVPlayer will keep a reference to it and you can't reuse it with another player anyway.
  3. Once it's done playing, immediately destroy AVPlayer...release it, set its var to nil, whatever you need to do. **
  4. Now you can record. AVPlayer doesn't exist, so AVAudioRecorder is free to do its thing.
  5. When you're ready to play again, re-instantiate AVPlayerItem with the asset you've already loaded & AVPlayer. Again, this is trivial. The asset has already been loaded so there shouldn't be a delay.

** Note that destroying AVPlayer may take more than just releasing it and setting its var to nil. Most likely, you've also added a periodic time observer to keep track of the play progress. When you do this, you receive back an opaque object you're supposed to hold on to. If you don't remove this item from the player AND release it/set it to nil, AVPlayer will not dealloc. It appears that Apple creates an intentional retain cycle you must break manually. So before you destroy AVPlayer you need to (example):

[_player removeTimeObserver:_playerObserver];
[_playerObserver release]; //Only if you're not using ARC
 _playerObserver = nil;

As a side note, you may also have set up NSNotifications (I use one to determine when the player has completed playing)...don't forget to remove those as well.

1
votes

If you need use AVPlayer and AVAudioRecorder at the same time do following:

  1. set audio category to "play and record" (as described above or with C-based Audio Session Function)
  2. "record" method must be invoked after invocation of "play" method with some delay. (I set 0.5 sec)

If you don't provide this, playback will start, but recording will not start.

0
votes

shouldn't it be [AVAudioRecorder record]; instead of play?

0
votes

I've had the same issue: AVAudioRecorder did not start recording. My example is a bit different: I have a tabbar based app with several AVAudioPlayers in the different views. For the sake of the example, lets say that the first loaded view has 3 AVAudioPlayers, while the second view has one AVAudioPlayer and one AVAudioRecorder. They are all initiated in the viewDidLoad method.

The recorder was not working in second view. When I hit my record button, the view slightly shaked and the recording did not begin. Strangely, the simulator worked fine and did not show the same symptoms... (anyone has an idea why?)

After some research and reading this thread I have realized that I have not set the players in the first view to nil, so I guess they were still in the memory preventing the recorder to start. Once I have set them to nil in the viewDidDisappear method, it was working alright again.

here is what helped me:

-(void)viewDidDisappear:(BOOL)animated {
    firstPlayer = nil;
    secondPlayer = nil;
    thirdPlayer = nil;
}

ViewDidUnload did not help because the View, and therefore the variablbes in the class itself were not unloaded when switching views.

Hope this may be useful for others as well.

0
votes

@Aaron Hayman explanation is accurate but the solution for me was very simple. Just implement the didFinishPlaying method of the player delegate and release the player there.

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    self.playerWord = nil;
}