Video/audio streaming does not stop even if UIWebView is closed - iPad - uiwebview

I see this issue only on the iPad. The same things works as expected on the iPhone.
I am opening the URL from my application in a UIWebView. If the URL is a normal web page, it works fine as expected. But if the URL is that of a remote video/audio file, the UIWebView opens the default player which is again good.
Now when I dismiss the UIWebView (by clicking on the Done button on the player), the streaming doesn't stop and the audio/video keeps playing in the background (I cannot see it but it does keep playing in the background, can hear it). The UIViewController in which the webview was created is also dealloced (I put in a log statement in the dealloc method) but the streaming doesn't stop.
Can someone please help me out on why this could be happening? And how can I stop the audio/video streaming when the UIWebView is closed?
Thanks.

I have the same issue as stated but in this case the video that won't stop playing is a Youtube video embeded using the object/embed method.
I spent a long time trying to figure out how to get the video to stop playing and the only solution I found was to tell the UIWebView to load a blank page before dismissing the view:
[self.webContent loadRequest:NSURLRequestFromString(#"about:blank")];
Edit(2015-05-12): As mentioned by #chibimai below, this answer by alloc_iNit works along the same lines but since my answer is from 5 years ago -- and his only 4 -- the linked answer may be more applicable. I no longer do iPhone dev work so I cannot determine which is better either way.

I also found this behaviour simply because the web view hadn't been released.

I know this is pretty old thread, but for the sake of new developers like me.
My scenario was: I was using split controller, with media list on master and player in the detail controller section
I faced the same issue and tried all sorts of workaround.
I finally figured it out that the problem was simple, I was holding the reference of the detail controller in my master, and was not releasing the earlier detail controller. A simple release in the master at the right place solved the issue.

I'm working on the same problem. I've found that if you define something like this in your script tag:
function stopVideo(){ video.pause(); }
window.onunload = stopVideo;
Then in your UIViewController, add in:
-(void)viewWillDisappear:(BOOL)animated{
[webView stringByEvaluatingJavaScriptFromString:#"window.onunload();"];
[super viewWillDisappear:animated];
}
It seems to try to pause/stop the video for several seconds, but then you hear the audio continue to play!
Update!
This is a general bug with the media player. You have to set the playback time to -1 in order to make it really stop.

I've just had an issues with an mp4 file opening automatically in media player from a web-page without possibility to close it - "Done" button was only seen in video fullscreen mode and just closed fullscreen, so I had no way to return to the webpage without adding a "Back" button to the navigation bar. (on iPhone the video was opening in a separate view with "Done" button taking back to the WebView with the source page)
The workaround that helped me is to open the video file in a separate media player.
catch opening an MP4 file
- (BOOL) webView: (UIWebView *) webView shouldStartLoadWithRequest: (NSURLRequest *) request navigationType: (UIWebViewNavigationType) navigationType
{
NSRange range = [request.URL.absoluteString rangeOfString: #".mp4" options: NSCaseInsensitiveSearch];
if ( range.location != NSNotFound ) //opening MP4 video file
{
[self showFullscreenMediaWithURL: request.URL];
return NO;
}
return YES;
}
where
- (void) showFullscreenMediaWithURL: (NSURL *) mediaURL
{
MPMoviePlayerViewController *ctrl = [[MPMoviePlayerViewController alloc] initWithContentURL: mediaURL];
ctrl.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController: ctrl animated: YES];
[ctrl release];
}
opens video with URL in a media player in a model view
don't forget to add MediaPlayer.framework to the project and import
#import <MediaPlayer/MediaPlayer.h>
for the project to be built
PS. many thanks to Viktor Gubrienko for the solution

In fact, I prefer to use the null link to solve the problem.
like this:
[self.webView loadRequest:NSURLRequestFromString(#"about:blank")];

thanks to your thread on this issue I was able to figure out a solution that resolves the issue completely, and I tested it on my IPad, not just the simulator. This also resolves the issue with the audio playing. This is not the permanent resolution but an effective work around.
Overview of the approach:
Basically all that is needed is to send a short audio file to the webView. I made a copy of the IMac submarine.m4v file, I called ping.m4v and added it to my xcode project in the resource folder.
Then at the point when I want the video / audio to stop I do the following steps:
webView.hidden = TRUE;
NSString *mimeType = [[NSString alloc] initWithString:#"video/x-m4v"];
NSData *PingData = [NSData alloc];
PingData = [NSData dataWithContentsOfFile:PingFilePath];
[webView loadData:PingData MIMEType:mimeType textEncodingName:nil baseURL:[NSURL URLWithString:PingFilePath]];
Now you also have to handle error "204". Error 204 seems to be just a warning saying that webView is going to handle this audio file. To handle the error I added this single line (see >>>) to didFailLoadWithError
(void)webView:(UIWebView *)BHwebView didFailLoadWithError:(NSError *)error
{
NSLog(#"Error %i", error.code);
if (error.code == NSURLErrorCancelled) return; // this is Error -999
if (error.code == 204) return; // this is Error 204 - for audio player in webview.
Finally I turn webView.hidden = FALSE right before I display my next Audio/Video. This way the little play bar for the sound file does not show on the display. There is just a soft ping sound... any audio file will do...
I hope this helps...

This is what I use. It does log this error "Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session." but does stop the audio file from playing (fading out) and remembers the play position.
AVAudioSession *s = [AVAudioSession sharedInstance];
if (s != nil)
[s setActive:NO error:nil];

As web view hadn't been released, the video keeps playing on the web view in background.
So you need to release the web view simply by using below code which works fine for me.
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:(BOOL)animated];
[self.wvwebview removeFromSuperview];
}
I hope this helps...

in my case using loadHTMLString and not loadRequest with #"about:blank", fix the problem on macOS
[self.wkWebView loadHTMLString:#"about:blank" baseURL:nil];

I had the same issue in Table view where the table view cell has a webview which loads an audio file . Once you play and go back to another screen the audio still plays .
Solution : reload the table view in viewWillDisappear(_:) of the table view controller and the audio will stop playing once you navigate to another screen .

Related

AUDIO_OUTPUT_FLAG_FAST denied by client OR E/MediaPlayer: Error (1,-19)

I am creating a game using Libgdx. I have a lot of small sounds files in the MP3-format and since it is so many I do not preload them. I only load the sound I want to play when it is to be used, like this:
actorSound = Gdx.audio.newSound(Gdx.files.internal(sound));
The code above works great, but the rest of my sounds do not unfortunately. The actor above has its own class and plays a different sound every time it is touched.
When I try to play sounds in my Gamescreen I get the following error:
AUDIO_OUTPUT_FLAG_FAST denied by client
All the audiofiles have the same properties and have been recorded using the same microphone & Audacity. The files are all 44100Hz and only a few kb in size each.
I wonder why the sounds the Actor plays work and the other sounds do not?
I decided to try to change the non-working sounds to music instead and now they play fine - for a little while that is. I can play a full game, return to the menu and start a new game again. The second time I start a game I only get 3 sounds from the Gamescreen and then it is silent except for the sounds from the actor. The error that appears looks like this:
E/MediaPlayer: Error (1,-19)
I load the Music just the same way as the Sound:
gameSound = Gdx.audio.newMusic(Gdx.files.internal(soundeffect));
I have looked into the two errors by reading posts like these:
AUDIO_OUTPUT_FLAG_FAST denied by client
Mediaplayer error (-19,0) after repeated plays
But I'm not sure what to do or change to solve my problem. I would prefer Sound if that is possible, but Music is an acceptable workaround...
When it comes to Music it is probably as suggested in the URL, that I do not release the media players. I am not sure how to do that?
When I leave the GameScreen for another screen, like the MenuScreen or RewardScreen, I dispose Music first. The other screens use Music as well and the sounds are loaded when needed. When I change back to the GameScreen I dispose again and then start a new game...
Any ideas or suggestions? Any help is greatly appreciated.
I added AssetManager as suggested, and I now have a loding screen that loads all the sounds. I load them as sounds and not music, which is how I prefer it.
The sounds work well in the actual game but once I get to the reward screen, only the first sound plays and after that the app crashes with the following error:
06-02 07:47:09.774 6208-6282E/AndroidRuntime: FATAL EXCEPTION: GLThread 2935
Process: PID: 6208
java.lang.NullPointerException: Attempt to read from field 'com.badlogic.gdx.assets.AssetManager
Assets.Assets.manager' on a null object reference
at DelayedSounds(RewardsScreen.java:538)
at RewardsScreen.Update(RewardsScreen.java:567)
at Screens.RewardsScreen.render(RewardsScreen.java:577)
at Game.render(Game.java:46)
at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(AndroidGraphics.java:459)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1562)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1262)
06-02 07:47:13.823 6208-6208 E/AndroidGraphics: waiting for pause
synchronization took too long; assuming deadlock and killing
The DelayedSounds method looks like this:
public void DelayedSounds(){
timer = timer + Gdx.graphics.getDeltaTime();
if(playitem == true && timer > 2){
itemsound = "vinster/" + item + ".mp3";
assets.manager.get(itemsound, Sound.class).play(volume);
//sound = Gdx.audio.newMusic(Gdx.files.internal(itemsound));
//sound.setVolume(volume);
//sound.play();
playitem = false;
}
if(playkeep == true && time > 3){
assets.manager.get("dialog/VINSTEN.mp3", Sound.class).play(volume);
//sound = Gdx.audio.newMusic(Gdx.files.internal("prizes/prize.mp3"));
//sound.setVolume(volume);
//sound.play();
playkeep = false;
}
}
As can be seen I use AssetManager now, and have commented out my old code for testing purposes. I define AssetManager in my main class and then pass it around to all other classes that uses sounds.
The RewardsScreen will only play the first sound and then it crashes on a NULL object reference as it seems.
If I change my code back to using Music in the RewardsScreen it works fine (see the code that is commmented out)
The sound I try to play is exactly the same in both cases. Assets class that handles the loading of all my assets has the sounds included and since I still get a NULL object I assume one or more items fails to load?
I search the logs and find this where it loads the sounds:
06-02 08:05:09.360 9844-9888/E/WVMExtractor: Failed to open libwvm.so: dlopen failed: library "libwvm.so" not found
06-02 08:05:09.395 9844-9888/ E/NdkMediaExtractor: sf error code: -1010
06-02 08:05:09.395 9844-9888/ E/SoundPool: Unable to load sample
Maybe this is related to my problem?
I load all the sounds in the same manner and most definitely seem to work, the loading is the regular:
manager.load("prizes/cash.mp3", Sound.class);
I still find the AUDIO_OUTPUT_FLAG_FAST denied by client in my logs but now the sounds are playing instead of being rejected.
Any more ideas about how to solve this?
Your link having enough information for your bug.
It's not good way to create Resource instance each time in Game, Create once and user All over your game, if possible use AssetManager.
Like create Music instance in onCreate() method of your game.
gameSound = Gdx.audio.newMusic(Gdx.files.internal(soundeffect));
Music having play(), resume() and many other helpful methods.
You can also take a look of this, it may be helpful.

UiWebView response to window.print

Is there a way to detect window.print from a UIWebView and respond to it? I have printing in my UiWebView and need to have a way to react to that event from the UIWebView.
First, this is the solution I came across. But UIWebview does not support those events and the API like in a desktop browser. And haven't yet got the matchMedia to work. Probably will work when I have a printer configured!?!.
The actual reason is that the media type of the web page did not change from screen to print in the UIWebView, unlike desktop browsers, so there is no trigger, till this point.
And UIWebView has no delegate method for printing exposed till date.
Anyways, where there is a will, there is a [hack] way. Two step solution.
Step 1. Javascript
If you have access to the HTML or JS file, and it is used only for this UIWebView, you can add the below lines to the JS
(function(){
var originalPrintFn = window.print; // Keep it cached in this variable, just in case
window.print = function(){
// Trigger location change
window.location = "your_app_scheme:print";
}
})();
If you don't have access to the HTML or JS file, in your UIWebView delegate, add the below code
[self.myWebView stringByEvaluatingJavaScriptFromString:#"(function(){var originalPrintFn = window.print;window.print = function(){window.location = 'your_app_scheme:print';}})();"];
This can be done in the delegate's webViewDidFinishLoad method
Step 2. Catching the call in native
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([[[request URL] absoluteString] hasPrefix:#"your_app_scheme:"]) {
// Call the given selector
[self performSelector:#selector(your_handle_to_print)];
// Cancel the event, so the webview doesn't load the url
return NO;
}
return YES;
}
After this in your_handle_to_print, you can do your printing magic.
Hope this helps!

Detecting Vibration on a Table with the iPhone

is it possible to detect vibration on the iPhone? I'm trying to figure out how to detect when the user smacks a desk or table when the phone is sitting on it. I remember reading somewhere you could detect a smack on a wooden table using the mic and AVFoundation. Any ideas?
Thanks
if you go down the microphone route, something like this might do the trick:
//somewhere during setup call this line
[anAVAudioRecorder setMeteringEnabled:YES];
//then in a method used to poll the audioMeter
[anAVAudioRecorder updateMeters];
averagePower = [anAVAudioRecorder averagePowerForChannel:0];
if (averagePower > threshold ) {
[musicPlayerClass play];
}

AudioQueueStart fail -12985

I made a streaming music player and it works fine in the foreground.
But in the background iOS4, it doesn't play the next song automatically. ( remote control works )
The reason is AudioQueueStart return -12985.
I already check the audio session. it just fine. I use AudioQueueStart when it start to play the music.
How can you remove AudioQueueStart?
- (void)play
{
[self setupAudioQueueBuffers]; // calcluate the size to use for each audio queue buffer, and calculate the // number of packets to read into each buffer
OSStatus status = AudioQueueStart(self.queueObject, NULL);
}
I read the answer in the web about the AudioQueueStart fail subject.
One thing to check is that the AudioSession is active first.
In my case, I had previously set the session to inactive between song changes before starting a new song:AudioSessionSetActive(false);
Once I removed this AudioQueueStart works just fine from the background.
In my experience, the -12985 message occurs because another app already has an audio session active when you try to start playback in your app. Options are to 1) instruct the user to close the other app, or 2) set mix mode (see kAudioSessionProperty_OverrideCategoryMixWithOthers).
The disadvantage of mix mode is if you depend on lock screen art or remote controls, they won't work with mix mode set.
I also faced with such problem week ago. I've spent two days to find solution and I found it. May be this link will help (it is official answer): http://developer.apple.com/library/ios/#qa/qa1668/_index.html
Make sure that you activate session from applicationDidEnterBackground task handler. Now my application can play sound in background.
See this.
You probably need to include the following:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
Towards the bottom there is a reiteration of the how important that line is. As it is not mentioned in any of the three main audio audio guides (AVFoundation, AudioSession, or AudioQueue) it can easily be missed.
I have the same problem.
I registry the AudioSessionInterruptionListener, pause the audio when phone call, resume it after the call end. but get -12985 error code when call AudioQueueStart to resume.
My solution is that I try to call AudioQueueStart after 0.02s.
I don't know the reason.
On iOS7, AudioQueueStart was returning '!int' ('tni!'), though i'm sure no one would be surprised to find that it's not documented in the docs or headers. It was the same issue, though, and the same fix (setting the audio session to active in the background task handler) worked for me.

Playing a sound in a Firefox add-on

I would like to create a simple add-on that would play a different MP3 recording every time the user double clicks a word in a webpage he is visiting and selects a special option from the context menu.
The MP3 files are located on a remote server. Normally I would use JavaScript+Flash to play the MP3 file. In a Firefox add-on, however, I'm unable to load external scripts for some reason (playing the sound works fine if it's the webpage itself that loads the scripts, but of course I need it to work with every website and not just the ones that include the script).
So what's the easiest way to play a remote MP3 file in a Firefox add-on using JavaScript?
This may not entirely solve your question, as I don't BELIEVE it plays MP3s, but I'm not certain.
Firefox has nsISound, which I KNOW can play remote WAV files, as I've tested and proved it.
You may want to test it for yourself and see if it leads you a little closer!
var ios = Components.classes['#mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService);
var sound = ios.newURI("http://www.yoursite.com/snds/haha.wav", null, null);
var player = Components.classes["#mozilla.org/sound;1"].createInstance(Components.interfaces.nsISound);
player.play(sound);
Good luck, I hope this at least gets you close!
I know this is an old question, but if someone needs a way to do it:
let player = document.createElement("audio");
player.src = browser.runtime.getURL(SOUND_URL);
player.play();
There is one caveat: the user must have allowed autoplay on the website.
Here is a working code....
var sound = Components.classes["#mozilla.org/sound;1"].createInstance(Components.interfaces.nsISound);
var soundUri = Components.classes['#mozilla.org/network/standard-url;1'].createInstance(Components.interfaces.nsIURI);
soundUri.spec = "chrome://secchat/content/RING.WAV";
sound.play(soundUri);
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
var audio = ('http://example.com/audio.mp3');
audio.play();

Resources