I'd like to start of by apologizing for this ridiculously long post, but I tried to provide as much code and data as possible for you to refer to.
I am working on a video project where I will need the flexibility of the AVQueuePlayer (foremost the ability to scale and layout the video layer freely, as well as creating my customized controls). Problem is, I need to be able to mix video content of different types (in this case progressive download .mp4 and http streaming .m3u8). Here's where things get funky.
First some code to get you going:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *assetsArray = [NSArray arrayWithObjects:
[AVPlayerItem playerItemWithURL: [NSURL URLWithString: kTestPrerollURL]], // .mp4-file on server
[AVPlayerItem playerItemWithURL: [NSURL URLWithString: kTestVideoURL]], // .m3u8-file on server
[AVPlayerItem playerItemWithURL: [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource: @"2" ofType:@"m4v"]]], // local m4v-file
[AVPlayerItem playerItemWithURL: [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource: @"3" ofType:@"m4v"]]], // local m4v-file
nil];
// Add KVO observation on each player item to track when they are ready for playback
for(id playerItem in assetsArray) {
[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];
}
[self setPlayer: [AVQueuePlayer queuePlayerWithItems: assetsArray]];
[self setPlayerLayer: [AVPlayerLayer playerLayerWithPlayer: [self player]]];
[[self playerLayer] setFrame: [playerView bounds]];
// Additional layer customization...
[[playerView layer] addSublayer: [self playerLayer]];
[playerView setBackgroundColor: [UIColor clearColor]];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: nil];
}
And when the AVPlayerItem changes status, this method is invoked:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
// A bunch of loggers...
if([(AVPlayerItem *)object status] == AVPlayerItemStatusReadyToPlay) {
CGSize movieSize = [[[player currentItem] asset] naturalSize];
CGRect playerRect = [playerView frame];
playerRect.size.height = playerRect.size.width * movieSize.height/movieSize.width + 7.0;
[playerView setFrame: playerRect];
[playerLayer setFrame: [playerView bounds]];
[player play];
}
}
If the user chooses to press a "Next"-button, the player skips to the next clip:
- (IBAction)next:(id)sender {
NSLog(@"------ USER SWITCHED TO NEXT ------");
AVPlayerItem *object = (AVPlayerItem *)[player currentItem];
[object removeObserver:self forKeyPath: @"status"];
[player advanceToNextItem];
}
Finally, when the AVPlayerItem reaches the end, this method is invoked:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
NSLog(@"------ PLAYER ITEM DID REACH END ------");
AVPlayerItem *object = (AVPlayerItem *)[notification object];
[object removeObserver:self forKeyPath: @"status"];
}
Ok, so my problem is that mixing different formats (in this case the m3u8-file) causes some funky behavior.
If I load the m3u8-file first in the queue, it plays properly, but if I advanceToNextItem the app crashes. NSZombieEnabled reports:
*** -[Not A Type retain]: message sent to deallocated instance 0x684dfe0
[Switching to process 67957]
And the call stack shows a release out of my bounds (MyPlayer is the name of the project):
po [NSThread callStackSymbols]
2011-01-05 16:12:57.151 MyPlayer[67957:6303] *** __NSAutoreleaseNoPool(): Object 0x56a0580 of class _NSCallStackArray autoreleased with no pool in place - just leaking
<_NSCallStackArray 0x56a0580>(
0 MyPlayer 0x00002154 start + 0,
1 CoreFoundation 0x00e72f22 _CF_forwarding_prep_0 + 50,
2 CoreFoundation 0x00e140bc CFRetain + 92,
3 CFNetwork 0x014c436e _ZN21XConnectionEventQueueI12XLoaderEvent18XLoaderEventParamsE10pushEventsEP20XConnectionEventInfoIS0_S1_El + 120,
4 CFNetwork 0x014c4252 _ZN19URLConnectionLoader16pushEventsLockedEP20XConnectionEventInfoI12XLoaderEvent18XLoaderEventParamsEl + 120,
5 CFNetwork 0x014c41c7 _ZN19URLConnectionLoader10pushEventsEP20XConnectionEventInfoI12XLoaderEvent18XLoaderEventParamsEl + 57,
6 CFNetwork 0x015976b6 _ZN19URLConnectionClient25getRequestForTransmissionEhP14_CFURLResponsePK13_CFURLRequestPP9__CFError + 942,
7 CFNetwork 0x014c3030 _ZN19URLConnectionClient22_clientWillSendRequestEPK13_CFURLRequestP14_CFURLResponsePNS_26ClientConnectionEventQueueE + 230,
8 CFNetwork 0x01597789 _ZN19URLConnectionClient26ClientConnectionEventQueue33processAllEventsAndConsumePayloadEP20XConnectionEventInfoI12XClientEvent18XClientEventParamsEl + 141,
9 CFNetwork 0x014c2e3c _ZN19URLConnectionClient13processEventsEv + 100,
10 CFNetwork 0x014c2cb7 _ZN17MultiplexerSource7performEv + 251,
11 CoreFoundation 0x00ee301f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15,
12 CoreFoundation 0x00e4128b __CFRunLoopDoSources0 + 571,
13 CoreFoundation 0x00e40786 __CFRunLoopRun + 470,
14 CoreFoundation 0x00e40240 CFRunLoopRunSpecific + 208,
15 CoreFoundation 0x00e43504 CFRunLoopRun + 84,
16 CoreMedia 0x00cb39af FigThreadGlobalNetworkBufferingRunloop + 149,
17 CoreMedia 0x00cb624a figThreadMain + 308,
18 libSystem.B.dylib 0x9473885d _pthread_start + 345,
19 libSystem.B.dylib 0x947386e2 thread_start + 34
)
if the .m3u8-file is placed somewhere after the first position in the queue, I get a status AVPlayerItemStatusFailed, and the clip skips to the next clip in the queue.
I realize this might be related to some memory management, but at the same time I start doubting it. However, it is quite evident I am doing something wrong, so every suggestion of a solution is more than welcome.
You can't do this - AVPlayer can either play HLS content (M3U) or non-streamed, but not a mix. See this for details: https://devforums.apple.com/message/570406#570406
User contributions licensed under CC BY-SA 3.0