I am working on the livestream feature for an iOS application. I have used an AVPlayerViewController contained within a ContainerView to display the video.
And here is the associated code for the ViewController
#interface ViewController ()
#property MPMoviePlayerController* streamPlayer;
#property BOOL isPlaying;
#end
AVPlayerViewController *streamPlayer;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.isPlaying = NO;
NSURL *streamURL = [NSURL URLWithString:#"http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch1/appleman.m3u8"];
streamPlayer = [[ UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"StreamPlayer" ];
streamPlayer.player = [ AVPlayer playerWithURL:streamURL ];
}
- (IBAction)playVideo:(id)sender{
if ( !self.isPlaying ){
[ streamPlayer.player play ];
[self.button setTitle:#"Stop" forState:UIControlStateNormal ];
self.isPlaying = YES;
} else {
[ streamPlayer.player pause ];
[self.button setTitle:#"Play" forState:UIControlStateNormal ];
self.isPlaying = NO;
}
}
(You can ignore the isPlaying variable. It's just to see what I can control)
The stream starts and works fine for a few minutes before it stops/buffers(i think) for a few seconds and an error show up:
ERROR: 849: AudioQueue: request to trim 4291961269 + 0 = 4291961269 frames from buffer containing 21504 frames
The stream continues after this but there is no audio.
Occasionally there is a second or two of audio but immediately the error showing up again and the stream continues in silence. (Something I'm glad of when a Pitbull video is being broadcast. )
If I pause the stream and then play it, the audio returns for a few minutes and then goes off again.
I have searched for this error but have not found anything to help me fix this. Being relatively new to iOS and HLS, I don't have any ideas as to what the issue could be but if I had to guess I'd say that it's an issue with synchronization of audio and video.
Any and all help is appreciated.
There are two issues in your code that stand out right away.
You have TWO streamPlayer variables.
The AVPlayerViewController variable should be a Global Variable
By putting your AVPlayerViewController property declaration inside your Interface Extension as a global variable the error should be resolved.
The code would look something like this:
#interface ViewController ()
#property (nonatomic) MPMoviePlayerController *mpStreamPlayer;
#property (nonatomic) AVPlayerViewController *avStreamPlayer;
#property (nonatomic) BOOL isPlaying;
#end
#implementation ViewController
- (void)viewDidLoad ... // continue as expected
The reason I am assuming it will be resolved is because of this similar problem a member of the community is experiencing with the Error 849 (their codebase is SWIFT) and the answer working answer.
this is possibly related to the iOS 9 bugs, paste your m3u8 into the Safari, you could experience the same problem down the road (intermediate audio interrupt, audio/video out of sync), there is nothing you can do with your code.
see some report here, and Apple engineer ask to file a bug report.
Related
I'm developing a iOS app using objective-c. When the application is launched a background music is played. The background music should continue playing when the user clicks help button. Also when the user goes back to the main screen from the help screen the background music should be continuously playing.
For me a new background music is getting played along with the old background music when I navigate from help to main menu. So, I am hearing two background music now.
Could anyone help me in solving this issue?
Regards,
Bharathi.
Your problem could be solved if you retained a reference to your audio player in your UIApplicationDelegate (or some other singleton that's kept around).
//in the .h
#property (nonatomic, strong) AVAudioPlayer *player;
//in the .m
- (void) playMusic
{
if (self.player == nil) {
NSString *path = [NSString stringWithFormat:#"%#/music.mp3", [[NSBundle mainBundle] resourcePath]];
NSURL *soundUrl = [NSURL fileURLWithPath:path];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
}
if (!self.player.isPlaying) {
[self.player play];
}
}
That way you can call it wherever you need with a:
[(MyAppDelegate*)[UIApplication sharedApplication].delegate playMusic];
Though it might be to your advantage to keep around a SoundsManager class as a singleton in order to handle all the sounds that you'll need to track if you're going to need more than just this one.
I have implemented MobileVLCKit in iOS by using MobileVLCKit framework. I have an issue,
When I declare the player #Interface the streaming and voice is working well.
#import <MobileVLCKit/MobileVLCKit.h>
#interface ViewController ()<VLCMediaPlayerDelegate>{
VLCMediaPlayer *vlcPlayer1
}
#end
But, declare the VLCMediaPlayer object at local function the video preview not displayed but, audio id playing.
- (void)viewDidLoad {
[super viewDidLoad];
VLCMediaPlayer *vlcPlayer1 = [[VLCMediaPlayer alloc] initWithOptions:nil];
vlcPlayer1.drawable = view;
media = [VLCMedia mediaWithURL:[NSURL URLWithString: UrlString]];
[vlcPlayer1 setMedia:media];
[vlcPlayer1 play];
}
How can I resolve the issue. Because, I need to create the view dynamically.
Try this:
[vlcplayer.media addOptions:#{ #"network-caching" : #300}];
If it doesn't work, replace 300 with a bigger value.
That may work.
So both of these questions/answers put me on the right path, but this is ultimately what worked for me.
NSURL* _uri = [NSURL URLWithString:uri];
NSArray* initOptions = [NSArray arrayWithObjects:#"--codec=avcodec", "--network-caching=10000", nil];
self.player = [[VLCMediaPlayer alloc] initWithOptions:initOptions];
self.player.media = [VLCMedia mediaWithURL:_uri];
It looks like the "addOptions" is valid, but my particular use case wasn't picking it up, and instead I had to actually initialize the VLCMediaPlayer with the options from the get go. Worked out nice because it actually fits much better with other JAVA/Android/CMD line VLC api's.
I've done a lot of reading and research in trying to come up with a solution to my problem and can't seem to make my code work the way I want, or the way it should. What I want to do is be able to have different buttons play different, short sounds, without overlapping each other if I decide to click another sound button before another sound button's audio is finished. I've successfully got to the point where they play their designated sound using SystemSoundID, but they still overlap when I click on multiple buttons.
Below is a sample of my code:
Interface file:
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
}
-(IBAction)Sound:(id)sender;
#end
Implementation file:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
#implementation ViewController : UIViewController
- (IBAction)Sound:(id)sender
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"Sound" ofType:#"mp3"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[theAudio play];
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
I would appreciate the help more than you know, as I've spent quite some time trying to get it to work the way it should. If you have any questions for me please let me know! Thank you very much.
-Michael
I have the understanding that when you play an AudioSystemSound that it will play until completion. This method of playing Audio isn't a highly functional one and only really supposed to be used for simple "clicks" and "dings" etc. For a more comprehensive audio suite of features perhaps you could look at using the AVFoundation library with AVAudioPlayer where you can call "stop" to cease playing the called sound.
https://developer.apple.com/av-foundation/
Edited addition to the above answer:
In your .h file make sure you include;
#import AVFoundation;
and then create an AV player with;
AVAudioPlayer *playAudio
In your .m file add this block of code below in your method that needs to play the sound. Of course adjust the song/audiofile name and format appropriately;
NSString *backgroundMusic = [[NSBundle mainBundle]
pathForResource:#"insertSongNameHere" ofType:#"mp3"];
playAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:insertSongNameHere] error:NULL];
playAudio.numberOfLoops = -1;
[playAudio play];
Note: Number of loops being -1 means repeat forever. You can of course make that whatever number you like though. I hope that all helps, if not let me know and I'll see what I can do to assist!
I am having some EXC_BAD_ACCESS problems whilst trying to stop a video that is being played through MPMoviePlayerController. Here is some code:
Video Class:
#interface MyVideo()
#property (nonatomic, strong) MPMoviePlayerController * videoController
#end
#implementation MyVideo
#synthesize videoController;
- (MyVideo*) initIntoView: (UIView*) view withContent (NSDictionary*) contentDict {
self=[super init];
NSString * rawUrl=[[NSString alloc] initWithFormat:#"http://.../%#.mp4", [contentDict objectForKey:#"filename"]];
NSURL * videoUrl=[[NSURL alloc] initWithString:rawUrl];
videoController = [[MPMoviePlayerController alloc] initWithContentURL:videoUrl];
videoController.movieSourceType=MPMovieSourceTypeFile;
videoController.view.frame = viewRef.bounds;
[videoController.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
videoController.controlStyle=MPMovieControlStyleNone;
[view addSubview:videoController.view];
return self;
}
/* other code */
- (void) stop {
NSLog(#"video stop");
[videoController stop];
}
#end
This MyVideo class is a property within my AppDelegate class, like so:
#property (nonatomic, strong) MyVideo video;
A line in my AppDelegate class fires off the public method stop of this MyVideo class, like so:
[video stop];
This usually works fine. But occasionally I get an EXC_BAD_ACCESS error on the line with [videoController stop]. The line before it, the one with NSLog on it, outputs to the console as expected.
This crash happens while the video that has been loaded into the videoController is still playing. But it does not happen every time.
Can anyone suggest why this crash is happening? I suspect its because videoController is no longer in memory, despite it being strong and still in use.
Am I right in thinking there is absolutely no way of testing the videoController to see if it is still in memory?
Am I right in thinking there is absolutely no way of forcing videoController to stay in memory while it is being used to play a video?
So instead of trying to stop the video and shut down the MyVideo class properly when I dont want to play the video anymore, I am now thinking of just setting the MyVideo class to nil, and let ARC deal with stopping the video and clearing it from memory. Is this the right way of doing this? Would there be any disadvantages to this?
Are there any other solutions to this problem that I am missing?
With EXC_BAD_ACCESS my first port of call is to enable Zombie Objects in my Debug Scheme.
This should give you an idea of what object is causing the EXC_BAD_ACCESS. Just to double check it is your videoController.
When does your stop function get called on MyVideo
Is the crash on specific devices, iPad iPod, does it occur on specific os's iOS 6,7
Is it the same video file?
It cant randomly break there must be some pattern that is causing the EXC_BAD_ACCESS
I may be wrong but I have a feeling that its a threading issue. I suspect the thread calling the [myVideo stop] function is not aware of the videoController(probably initialised on the main thread). Try calling the [videoController stop] within the main thread by using the following:
dispatch_async(dispatch_get_main_queue(), ^{
[videoController stop];
});
Do let me know if this works!
I've been playing around with this for a day now and have hit a brick wall.
I want to have a UIScrollView that shows a series of videos that the user can scroll across. Setting up the UIScrollView is fine and each item within it (I'm calling them MenuItems) is a subclass of UiViewController that contains and manages all of the setup of the MPMoviePlayerController etc.
I discovered however that you can only have one MPMoviePlayerController in a window, playing at once.
So, I thought the best way to handle this is to have accessible methods on each MenuItem, stopVideo and startVideo that I would trigger as each menu item became the "focus" of the UiScrollView (I have successfully coded the delegate so that it captures the scroll event and works out which page is in the centre of the scrollview).
The problem is, I can't work out how to access the MenuItem objects in the UiScrollView.
I have the following code in the UIScrollView delegate to do this:
- (void)scrollViewDidScroll:(UIScrollView *)sView {
//establish what page we're on
static NSInteger previousPage = 0;
MenuItem *currentMenuItem;
MenuItem *previousMenuItem;
CGFloat pageWidth = sView.frame.size.width;
float fractionalPage = sView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
if (previousPage != page) {
//firstly, get the previous page and stop the video
previousMenuItem = [sView.subviews objectAtIndex:previousPage];
previousMenuItem = [previousMenuItem nextResponder];
[previousMenuItem hideVideo];
//[previousMenuItem release];
//page has changed, get the new current page and start the video
currentMenuItem = [sView.subviews objectAtIndex:page];
currentMenuItem = [currentMenuItem nextResponder];
[currentMenuItem showVideo];
//[currentMenuItem release];
previousPage = page;
}
}
The methods showVideo and hideVideo are called, but I get other errors (for example, I can't seem to initialize an MPMoviePlayerController inside the MenuItem without an SIGABRT error).
I'm thinking my methodology is flawed here or that there is a simpler approach? All help appreciated!
Many thanks.
bit late for a response but I got stuck on this as well and managed to bash out a solution so thought I'd include it for anyone else.
I think it has to do with memory allocation for the movie players.
I included this code in my .h file
#interface VideoInstructionViewController : UIViewController <UIScrollViewDelegate>
{
MPMoviePlayerController *player0;
MPMoviePlayerController *player1;
MPMoviePlayerController *player2;
}
and so that the movie players were effectively global and then just included this code in my .m file.
player0 = [[MPMoviePlayerController alloc] initWithContentURL:contentURL];
player1 = [[MPMoviePlayerController alloc] initWithContentURL:contentURL];
player2 = [[MPMoviePlayerController alloc] initWithContentURL:contentURL];
The rest of your code was very useful, I think it's a solid methodology, thanks!