I've been looking for a while online and have yet to find an answer to this question. I am looking to stream audio from a bluetooth microphone through the earbuds of the iPhone. I have been able to configure streaming from the microphone and streaming to the earbuds separately, but not simultaneously. I've done this by configuring the AudioSession to do such:
// Set up Audio Session
NSError *error = nil;
audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&error];
if (error) NSLog(#"Error in Setting Audio Session Category");
error = nil;
[audioSession setActive:YES error:&error];
if (error) NSLog(#"Error in Setting Audio Session Active");
error = nil;
for (AVAudioSessionPortDescription *mDes in audioSession.availableInputs){
printf("Port: %s\n",[mDes.portType UTF8String]);
if ([mDes.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
[audioSession setPreferredInput:mDes error:&error];
if (error != nil)
printf("Error Setting Preferred Input\n");
else
printf("Success\n");
error = nil;
}
}
It seems that when I set the preferred input, it also sets the output. I've tried overriding the output after the fact, but that resets the input. Is there a way to configure the Audio Session's inputs and outputs separately?
Thanks.
Related
I am trying to remove Audio from Video and i am using SCRecorder Class.
but still there is Audio play. So Is there a way to remove Audio from Video using SCRecorder Class.I try following Code in my Project.
SCRecorder *recorder = [SCRecorder recorder]; // You can also use +[SCRecorder sharedRecorder]
SCAudioConfiguration *audio = recorder.audioConfiguration;
// Whether the audio should be enabled or not
audio.enabled = NO;
[_player play];
IN SCRecoder Class you need to stop or comment
this bunch of code
// if (self.audioConfiguration.enabled) {
// if (_audioOutput == nil) {
// _audioOutput = [[AVCaptureAudioDataOutput alloc] init];
// [_audioOutput setSampleBufferDelegate:self queue:_audioQueue];
// }
//
// if ([session canAddOutput:_audioOutput]) {
// [session addOutput:_audioOutput];
// _audioOutputAdded = YES;
// } else {
// audioError = [SCRecorder createError:#"Cannot add audioOutput inside the sesssion"];
// }
// }
ann you find this code in below method
- (void)openSession:(void(^)(NSError *sessionError, NSError *audioError, NSError *videoError, NSError *photoError))completionHandler {
During the call I try to switch voice from internal speaker to Loud speaker on iOS device using pjsip 2.2 library. It returns TRUE as success, but physically it doesn't change sound destination.
I use the next code
- (BOOL)setLoud:(BOOL)loud {
if (loud) {
#try {
pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
pj_status_t pj_status = pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
&route, PJ_TRUE);
if (pj_status == PJ_SUCCESS) {
return YES;
}
else
{
return NO;
}
}
#catch (NSException *exception) {
return NO;
}
} else {
#try {
pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
pj_status_t pj_status = pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
&route, PJ_TRUE);
if (pj_status == PJ_SUCCESS) {
return YES;
}
else
{
return NO;
}
}
#catch (NSException *exception) {
return NO;
}
}
}
Could you suggest how can we make this work?
With the introduction of iOS 7, you should now be using AVAudioSession to handle any audio management. It took me a long time to finally get this to work but I finally figured out the problem of why my audio was not automatically routing to my iPhone Speaker. The problem is that when you answer a call, pjsip was automatically overriding the AVAudioSessionPortOverride I was performing before the call is answered. To tackle this problem, you simply just have to override the output audio port AFTER answering the call.
To make my VoIP application work efficiently with the background mode, I decided to handle the audio routing in a custom callback method named on_call_state. This method, on_call_state, is called by pjsip when a call state has changed. As you can read here, http://www.pjsip.org/pjsip/docs/html/group__PJSIP__INV.htm, there are many different flags you can check for when a call state has changed. The states I used in this example are PJSIP_INV_STATE_CONNECTING and PJSIP_INV_STATE_DISCONNECTED.
PJSIP_INV_STATE_CONNECTING is called when a audio call connects to another peer.
PJSIP_INV_STATE_DISCONNECTED is called when a audio call ends with another peer.
static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
{
pjsua_call_info ci;
PJ_UNUSED_ARG(e);
pjsua_call_get_info(call_id, &ci);
PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,
(int)ci.state_text.slen,
ci.state_text.ptr));
if (ci.state == PJSIP_INV_STATE_CONNECTING) {
BOOL success;
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&error];
if (!success) NSLog(#"AVAudioSession error setCategory: %#", [error localizedDescription]);
success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
if (!success) NSLog(#"AVAudioSession error overrideOutputAudioPort: %#", [error localizedDescription]);
success = [session setActive:YES error:&error];
if (!success) NSLog(#"AVAudioSession error setActive: %#", [error localizedDescription]);
} else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
BOOL success;
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
success = [session setActive:NO error:&error];
if (!success) NSLog(#"AVAudioSession error setActive: %#", [error localizedDescription]);
}
}
I'm currently working on an iPhone App, that only plays the Audio Track of an .mp4. The Player starts playing, but I can't hear any sound.
Here is the Code:
NSURL *videoUrl = [NSURL URLWithString:#"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"];
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack *track = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
AVURLAsset* videoAsset = [AVURLAsset URLAssetWithURL:videoUrl options:nil];
AVAssetTrack *audioTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];
NSError *error = nil;
BOOL success = [track insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:audioTrack atTime:kCMTimeZero error:&error];
if (!success)
{
NSLog(#"error: %#", error);
}
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:composition];
self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
[self.player play];
I had a similar problem and just found that AVFoundation is very unforgiving in how you format your composition. I eventually answered my own question - which was the same as your's here.
Check out my post:
iOS AVFoundation Export Session is missing audio
I have a GameCenter Sandbox-Account have tested my game, earned achievements, etc.
Now I've made some changes and want to test earning Achievements again!
Do I have to make an entire new Sandbox-Account or is there a way to reset my account?
The following code is from the Apple Documentation.
- (void) resetAchievements
{
// Clear all locally saved achievement objects.
achievementsDictionary = [[NSMutableDictionary alloc] init];
// Clear all progress saved on Game Center
[GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error)
{
if (error != nil)
// handle errors
}];
}
Also have a look at Apple's sample project GKTapper.
// Reset all the achievements for local player
- (void)resetAchievements
{
[GKAchievement resetAchievementsWithCompletionHandler: ^(NSError *error)
{
if (!error) {
[storedAchievements release];
storedAchievements = [[NSMutableDictionary alloc] init];
// overwrite any previously stored file
[self writeStoredAchievements];
} else {
// Error clearing achievements.
}
}];
}
I use Matt Gallagher's audio streamer for streaming radio stations. But how to record the audio? Is there a way to get the downloaded packets into NSData and save it in an audio file in the documents folder on the iPhone?
Thanks
Yes, there is and I have done it. My problem is being able to play it back IN the same streamer (asked elsewhere). It will play back with the standard AVAudioPlayer in iOS. However, this will save the data to a file by writing it out in the streamer code.
This example is missing some error checks, but will give you the main idea.
First, a call from the main thread to start and stop recording. This is in my viewController when someone presses record:
//---------------------------------------------------------
// 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
}
}
}
NOW, in AudioStreamer.m I need to handle the record setup call above
- (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
}
}
LASTLY, we need to modify the data portion of the streamer to actually save the bytes. You need to modify the stream code in the method: -(void)handleReadFromStream:(CFReadStreamRef)aStreameventType:(CFStreamEventType)eventType
Scroll down in that method until you find:
#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;
}
RIGHT after the length = line, add the following code:
//
// 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);
}
Here are the .h declarations:
Added to the interface for AudioStreamer.h
// for recording and saving a stream
NSString* filePath;
NSOutputStream* fileStream;
BOOL isRecording;
BOOL isPlayingFile;
In your view controller you will need:
#property(nonatomic, assign) IBOutlet UIButton* recordButtonImage;
#property(nonatomic, assign) BOOL isRecording;
#property (nonatomic, copy) NSString* recordFilePath;
Hope this helps someone. Let me know if questions, and always happy to hear someone who can improve this.
Also, someone asked about self.model.xxx Model is a Data Object I created to allow me to easily pass around data that is used by more than one object, and is also modified by more than one object. I know, global data is bad form, but there are times that just make it easier to access. I pass the data model to each new object when called. I save an array of channels, song name, artist name, and other stream related data inside the model. I also put any data I want to persist through launches here, like settings, and write this data model to a file each time a persistent data is changed. IN this example, you can keep the data locally. If you need help on the model passing, let me know.
OK, here is how I play back the recorded file. When playing a file, the station URL contains the path to the file. self.model.playRecordedSong contains a time value for how many seconds into the stream I want to play. I keep a dictionary of song name and time index, so I can jump into the recorded stream at the start of any song. Use 0 to start form the beginning.
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];
}
I hope this helps you play back the recorded file.
Try This Class This Have Full Solution OF All Radio streaming recording Playing All..
In Git Hub You Can Find This Use This Class Very Easy To Use