There is a function in my project to support playing a new audio file even in the background status.I use the MPMoviePlayerController class to play audio files.
But I found it was started in the active status,then it would continue playing the same audio(if the audio file wasn't finished) in the background status;But it won't start a new audio in the background status.
How can I to support playing a new audio in the backgound status?
I found the music player of the iphone can support playing a new audio in the background status,when set runloop or random PlayMode.
The following is my code to prove the appearance in fact:
(void)applicationDidEnterBackground:(UIApplication *)application
{
if (pCotnroller1) {
[pCotnroller1 release];
}
pCotnroller1 = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:#"http://r.yhiker.com:9001/0086/32/05/060009/Data/6/00863205060009006_24Kbps.mp3"]];
pCotnroller1.shouldAutoplay = YES;
[pCotnroller1 play];
//it wasn't played here
}
(void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
if (pCotnroller1) {
switch (pCotnroller1.playbackState) {
case MPMoviePlaybackStateStopped:
NSLog(#"播放地址");
break;
case MPMoviePlaybackStatePaused:
NSLog(#"播放暂停");
break;
case MPMoviePlaybackStateInterrupted:
NSLog(#"播放中断");
break;
case MPMoviePlaybackStateSeekingBackward:
break;
case MPMoviePlaybackStateSeekingForward:
break;
default:
break;
}
if (pCotnroller1.playbackState == MPMoviePlaybackStatePlaying) {
//it will come here,and play autoed here
// [pCotnroller1 play];
}
else
{
}
}
}
I tried some ways which was found in the net,but it didn't work.Some one had used this way to solute his question.I didn’t know why.
the following is my code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[application beginReceivingRemoteControlEvents];
[self updateUserLocation];
}
- (void)updateUserLocation
{
if (!pCotnroller1) {
// pCotnroller1 = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:#"http://r.yhiker.com:9001/0086/32/05/PP0041/Data/13/00863205PP0041013_24Kbps.mp3"]];
NSString *audioDiskPath = [HelpTools returnSpotAudioDiskPath:#"00863205PP0041013"];
pCotnroller1 = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:audioDiskPath/*#"/var/mobile/Applications/E7A06CB2-0856-407D-ABA1-2A846B65CD9D/Library/Caches/Data/0086/32/05/PP0041/Data/13/00863205060005016_24Kbps.mp3"*/]];
pCotnroller1.shouldAutoplay = YES;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
[pCotnroller1 play];
NSLog(#"进入后台");
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier taskID = [app beginBackgroundTaskWithExpirationHandler:nil];
if (bgTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:bgTask];
NSLog(#"允许后台播放");
}
bgTask = taskID;
}
else
{
NSLog(#"飞后台进行播放");
[pCotnroller1 play];
}
}
}
the console print:
2013-03-18 16:20:11.844 Sz[1665:907] [MPAVController] Autoplay: Disabling autoplay for pause
2013-03-18 16:20:11.845 Sz[1665:907] [MPAVController] Autoplay: Disabling autoplay
2013-03-18 16:20:13.489 Sz[1665:907] 文件存在
2013-03-18 16:20:18.017 Sz[1665:907] [MPAVController] Autoplay: Skipping autoplay, disabled (for current item: 1, on player: 0)
2013-03-18 16:20:18.998 Sz[1665:907] 进入后台
2013-03-18 16:20:21.269 Sz[1665:907] 播放地址
2013-03-18 16:20:26.251 Sz[1665:907] [MPAVController] Autoplay: Enabling autoplay
2013-03-18 16:20:26.307 Sz[1665:907] [MPAVController] Autoplay: Likely to keep up or full buffer: 0
2013-03-18 16:20:26.309 Sz[1665:907] [MPAVController] Autoplay: Skipping autoplay, not enough buffered to keep up.
2013-03-18 16:20:26.319 Sz[1665:907] [MPAVController] Autoplay: Enabling autoplay
2013-03-18 16:20:26.338 Sz[1665:907] [MPCloudAssetDownloadController] Prioritization requested for media item ID: 0
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 {
In my project, I use AVAudioSession to detect any headphone is plugged or unplugged. But in this case, I can't detect when bluetooth device is plugged. Here is my code for headphone state.
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
//NSLog(#"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
NSLog(#"Headphone/Line plugged in");
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-on.png"] forState:UIControlStateNormal];
_headSetState=YES;
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
NSLog(#"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
NSLog(#"Headphone/Line was pulled. Stopping player....");
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-off.png"] forState:UIControlStateNormal];
if(_isPlaying==YES)
{
[self.player pause];
[_audioButtonOutlet setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateNormal];
_isPlaying=NO;
}
_headSetState=NO;
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start - also when other audio wants to play
NSLog(#"AVAudioSessionRouteChangeReasonCategoryChange");
break;
}
- (BOOL)isHeadsetPluggedIn
{
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
{
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-on.png"] forState:UIControlStateNormal];
_headSetState=YES;
return YES;
}
else
{
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-off.png"] forState:UIControlStateNormal];
_headSetState=NO;
return NO;
}
}
return NO;
}
}
- viewWillAppear {
[AVAudioSession sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
[self isHeadsetPluggedIn];
}
So how can I detect if a bluetooth headset plugged or not iOS 8?
You can detect currently active bluetooth output devices (instead of input devices)
Swift Code:
import AVFoundation
func bluetoothAudioConnected() -> Bool{
let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
for output in outputs{
if output.portType == AVAudioSessionPortBluetoothA2DP || output.portType == AVAudioSessionPortBluetoothHFP || output.portType == AVAudioSessionPortBluetoothLE{
return true
}
}
return false
}
Bluetooth devices are based on the following question: What's the difference among AVAudioSessionPortBluetoothHFP, A2DP and LE?
I hope it helps someone
Edit for Swift 5.1 (Thanks iago849 for the fix)
var bluetoothDeviceConnected: Bool {
!AVAudioSession.sharedInstance().currentRoute.outputs.compactMap {
($0.portType == .bluetoothA2DP ||
$0.portType == .bluetoothHFP ||
$0.portType == .bluetoothLE) ? true : nil
}.isEmpty
}
I was able to detect whether a bluetooth headset (HFP) device was currently connected using the following:
NSArray *arrayInputs = [[AVAudioSession sharedInstance] availableInputs];
for (AVAudioSessionPortDescription *port in arrayInputs)
{
if ([port.portType isEqualToString:AVAudioSessionPortBluetoothHFP])
{
bHas = YES;
break;
}
}
However, your AVAudioSession category must be set as AVAudioSessionCategoryPlayAndRecord in order for this to work. If it isn't, the port will not show up in the list even if the HFP device is connected.
You can detect it with routeChangeNotification:
func activateHeadPhonesStatus(){
NotificationCenter.default.addObserver(self, selector: #selector(audioRouteChangeListener(_:)), name: AVAudioSession.routeChangeNotification, object: nil)
}
#objc func audioRouteChangeListener(_ notification:Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
if reason == .newDeviceAvailable {
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.bluetoothA2DP {
print("Bluetooth Headphone Connected")
break
}
}
}
Is it possible to play a track when the app has exited/gone into a background state?
For example:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSURL *trackURL = [NSURL URLWithString:#"spotify:track:489K1qunRVBm2OpS4XGLNd"];
[[SPSession sharedSession] trackForURL:trackURL callback:^(SPTrack *track) {
if (track != nil) {
[SPAsyncLoading waitUntilLoaded:track timeout:kSPAsyncLoadingDefaultTimeout then:^(NSArray *tracks, NSArray *notLoadedTracks) {
[self.playbackManager playTrack:track callback:^(NSError *error) {
if (error) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Cannot Play Track"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
self.currentTrack = track;
}
}];
}];
}
}];
}
The above code was taken from the Simple Player app provided with cocoalibspotify.
Copying and pasting code like this won't get you far when backgrounding - you can't use UI elements like UIAlertView, for a start.
You need to declare your application as supporting the audio background mode as documented by Apple. Then, when you go into the background you should make a background task to start playback.
I have a music button in the main UI of iOS and I want the button function as stop music button and at the same time can play the music again. My problem is I can only stop the music but can't play it again. Here is my iOS code in xcode4. How can I define one button for both play and stop music button? I have assigned one button for both stopMusic and playMusic but it doesn't work.
- (IBAction)stopMusic{
self.playBgMusic.enabled = YES;
[self.player stop];
}
- (IBAction)playMusic{
self.playBgMusic.enabled = YES;
[self.player play];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"music" ofType:#"mp3"];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
player.delegate = self;
[player play];
player.numberOfLoops = -1;
[super viewDidLoad];
}
Rather than have a single button try to send two different messages (stopMusic, playMusic), have it send toggleMusic every time. The toggleMusic method can then stop or play the music based on the current state of the player.
- (IBAction)toggleMusic{
if ([self.player isPlaying] == YES) {
[self.player stop];
} else {
[self.player play];
}
self.playBgMusic.enabled = YES;
}
- (IBAction)triggerSound {
if (player == nil) {
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
} else if ([player isPlaying]) {
[player stop];
player.currentTime = 0;
}
if (![player play]) {
NSLog(#"fail");
}
}
Where player is AVAudioPlayer and this action is linked with a UIButton. The sound can be played, and can be played again after the sound finish, but when I press the button again during the playback, the message "fail" is logged and the sound cannot be played, without any exception.
I and using iPad with iOS 4.2.1.
I have noticed that the audio stream fails to play after calling stop(which according to the reference undoes what prepareToPlay and play does).
Instead of calling stop, setting currentTime to player.duration(end of the stream) worked.