diff --git a/examples/ios/FSPlayerDemo/ViewController/FSMoviePlayerViewController.m b/examples/ios/FSPlayerDemo/ViewController/FSMoviePlayerViewController.m index b46e4d53..b25aee60 100644 --- a/examples/ios/FSPlayerDemo/ViewController/FSMoviePlayerViewController.m +++ b/examples/ios/FSPlayerDemo/ViewController/FSMoviePlayerViewController.m @@ -403,40 +403,55 @@ - (void)mediaIsPreparedToPlayDidChange:(NSNotification*)notification - (void)moviePlayBackStateDidChange:(NSNotification*)notification { - // MPMoviePlaybackStateStopped, - // MPMoviePlaybackStatePlaying, - // MPMoviePlaybackStatePaused, - // MPMoviePlaybackStateInterrupted, - // MPMoviePlaybackStateSeekingForward, - // MPMoviePlaybackStateSeekingBackward - - switch (_player.playbackState) - { - case FSPlayerPlaybackStateStopped: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: stoped", (int)_player.playbackState); + switch (self.player.playbackSchedule) { + case FSPlayerPlaybackScheduleIdle: + NSLog(@"FSPlayerPlaybackSchedule:Idle"); break; - } - case FSPlayerPlaybackStatePlaying: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: playing", (int)_player.playbackState); + case FSPlayerPlaybackScheduleInitialized: + NSLog(@"FSPlayerPlaybackSchedule:Initialized"); break; - } - case FSPlayerPlaybackStatePaused: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: paused", (int)_player.playbackState); + case FSPlayerPlaybackSchedulePreparing: + NSLog(@"FSPlayerPlaybackSchedule:Preparing"); break; - } - case FSPlayerPlaybackStateInterrupted: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: interrupted", (int)_player.playbackState); + case FSPlayerPlaybackSchedulePrepared: + NSLog(@"FSPlayerPlaybackSchedule:Prepared"); + break; + case FSPlayerPlaybackScheduleStarted: + NSLog(@"FSPlayerPlaybackSchedule:Started"); + break; + case FSPlayerPlaybackSchedulePaused: + NSLog(@"FSPlayerPlaybackSchedule:Paused"); + break; + case FSPlayerPlaybackScheduleCompleted: + NSLog(@"FSPlayerPlaybackSchedule:Completed"); + break; + case FSPlayerPlaybackScheduleStopped: + NSLog(@"FSPlayerPlaybackSchedule:Stopped"); + break; + case FSPlayerPlaybackScheduleError: + NSLog(@"FSPlayerPlaybackSchedule:Error"); + break; + } + + switch (self.player.playbackState) { + case FSPlayerPlaybackStatePaused: + NSLog(@"FSPlayerPlaybackState:Paused"); + break; + case FSPlayerPlaybackStatePlaying: + NSLog(@"FSPlayerPlaybackState:Playing"); + break; + case FSPlayerPlaybackStateStopped: + NSLog(@"FSPlayerPlaybackState:Stopped"); + break; + case FSPlayerPlaybackStateInterrupted: + NSLog(@"FSPlayerPlaybackState:Interrupted"); break; - } case FSPlayerPlaybackStateSeekingForward: - case FSPlayerPlaybackStateSeekingBackward: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: seeking", (int)_player.playbackState); + NSLog(@"FSPlayerPlaybackState:SeekingForward"); break; - } - default: { - NSLog(@"FSPlayerPlaybackStateDidChange %d: unknown", (int)_player.playbackState); + case FSPlayerPlaybackStateSeekingBackward: + NSLog(@"FSPlayerPlaybackState:SeekingBackward"); break; - } } [self.mediaControl refreshMediaControl]; diff --git a/ijkmedia/ijkplayer/ijkplayer.c b/ijkmedia/ijkplayer/ijkplayer.c index 4c8f312a..d12487cf 100755 --- a/ijkmedia/ijkplayer/ijkplayer.c +++ b/ijkmedia/ijkplayer/ijkplayer.c @@ -50,6 +50,7 @@ inline static void ijkmp_destroy(IjkMediaPlayer *mp) SDL_WaitThread(mp->msg_thread, NULL); mp->msg_thread = NULL; } + mp->weak_thiz = NULL; pthread_mutex_destroy(&mp->mutex); @@ -137,7 +138,9 @@ void *ijkmp_set_inject_opaque(IjkMediaPlayer *mp, void *opaque) assert(mp); MPTRACE("%s(%p)\n", __func__, opaque); + pthread_mutex_lock(&mp->mutex); void *prev_weak_thiz = ffp_set_inject_opaque(mp->ffplayer, opaque); + pthread_mutex_unlock(&mp->mutex); MPTRACE("%s()=void\n", __func__); return prev_weak_thiz; } @@ -147,7 +150,9 @@ void *ijkmp_set_ijkio_inject_opaque(IjkMediaPlayer *mp, void *opaque) assert(mp); MPTRACE("%s(%p)\n", __func__, opaque); + pthread_mutex_lock(&mp->mutex); void *prev_weak_thiz = ffp_set_ijkio_inject_opaque(mp->ffplayer, opaque); + pthread_mutex_unlock(&mp->mutex); MPTRACE("%s()=void\n", __func__); return prev_weak_thiz; } @@ -392,8 +397,6 @@ static int ijkmp_prepare_async_l(IjkMediaPlayer *mp) ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING); - // released in msg_loop - ijkmp_inc_ref(mp); mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop"); // msg_thread is detached inside msg_loop // TODO: 9 release weak_thiz if pthread_create() failed; @@ -663,10 +666,10 @@ void *ijkmp_get_weak_thiz(IjkMediaPlayer *mp) void *ijkmp_set_weak_thiz(IjkMediaPlayer *mp, void *weak_thiz) { + pthread_mutex_lock(&mp->mutex); void *prev_weak_thiz = mp->weak_thiz; - mp->weak_thiz = weak_thiz; - + pthread_mutex_unlock(&mp->mutex); return prev_weak_thiz; } diff --git a/ijkmedia/wrapper/apple/FSPlayer.m b/ijkmedia/wrapper/apple/FSPlayer.m index 71352acc..78a817b9 100644 --- a/ijkmedia/wrapper/apple/FSPlayer.m +++ b/ijkmedia/wrapper/apple/FSPlayer.m @@ -47,10 +47,13 @@ // It means you didn't call shutdown if you found this object leaked. @interface FSWeakHolder : NSObject -@property (nonatomic, weak) id object; +@property (nonatomic, weak) FSPlayer *player; @end @implementation FSWeakHolder +- (void)dealloc { + av_log(NULL, AV_LOG_DEBUG, "FSWeakHolder dealloc\n"); +} @end @interface FSPlayer() @@ -88,6 +91,8 @@ @implementation FSPlayer { __weak NSTimer *_playbackTimeNotifiTimer; NSTimeInterval _playbackTimeNotifiInterval; + + __weak FSWeakHolder *_weakHolder; } @synthesize view = _view; @@ -126,6 +131,7 @@ static void FSPlayerSafeDestroy(FSPlayer *player) { __block FSSDLHudControl *hudCtrl = player->_hudCtrl; __block UIView *view = player->_view; __block NSTimer *playbackTimeNotifiTimer = player->_playbackTimeNotifiTimer; + __block FSWeakHolder *weakHolder = player->_weakHolder; #if TARGET_OS_IOS [player unregisterApplicationObservers]; @@ -136,6 +142,7 @@ static void FSPlayerSafeDestroy(FSPlayer *player) { player->_view = nil; player->_hudCtrl = nil; player->_playbackTimeNotifiTimer = nil; + player->_weakHolder = nil; player->_segmentOpenDelegate = nil; player->_tcpOpenDelegate = nil; @@ -143,10 +150,6 @@ static void FSPlayerSafeDestroy(FSPlayer *player) { player->_liveOpenDelegate = nil; player->_nativeInvokeDelegate = nil; - ijkmp_set_weak_thiz(mediaPlayer, NULL); - ijkmp_set_inject_opaque(mediaPlayer, NULL); - ijkmp_set_ijkio_inject_opaque(mediaPlayer, NULL); - void(^UIHandler)(void) = ^{ [FSMediaModule sharedModule].mediaModuleIdleTimerDisabled = NO; /// VideoRendering在父视图移除 @@ -179,6 +182,13 @@ static void FSPlayerSafeDestroy(FSPlayer *player) { ijkmp_stop(mediaPlayer); ijkmp_shutdown(mediaPlayer); ijkmp_dec_ref_p(&mediaPlayer); + + // ijk资源全部释放完毕后,再销毁weakHolder,避免野指针 + // WeakHolder引用计数-1 + if (weakHolder) { + CFRelease((__bridge CFTypeRef)weakHolder); + weakHolder = nil; + } }); } @@ -220,12 +230,17 @@ - (void)_initWithContent:(NSURL *)aUrl // init player _mediaPlayer = ijkmp_ios_create(media_player_msg_loop); _msgPool = [[FSPlayerMessagePool alloc] init]; - FSWeakHolder *weakHolder = [FSWeakHolder new]; - weakHolder.object = self; - - ijkmp_set_weak_thiz(_mediaPlayer, (__bridge_retained void *) self); - ijkmp_set_inject_opaque(_mediaPlayer, (__bridge_retained void *) weakHolder); - ijkmp_set_ijkio_inject_opaque(_mediaPlayer, (__bridge_retained void *)weakHolder); + + FSWeakHolder *weakHolder = [[FSWeakHolder alloc] init]; + weakHolder.player = self; + _weakHolder = weakHolder; + + // WeakHolder引用计数+1 + void *retainWeakHolder = (void *)CFRetain((__bridge CFTypeRef)weakHolder); + ijkmp_set_weak_thiz(_mediaPlayer, retainWeakHolder); + ijkmp_set_inject_opaque(_mediaPlayer, retainWeakHolder); + ijkmp_set_ijkio_inject_opaque(_mediaPlayer, retainWeakHolder); + ijkmp_set_option_int(_mediaPlayer, FSMP_OPT_CATEGORY_PLAYER, "start-on-prepared", _shouldAutoplay ? 1 : 0); _view = _videoRendering = videoRendering; @@ -357,6 +372,8 @@ - (id)initWithMoreContent:(NSURL *)aUrl - (void)dealloc { FSPlayerSafeDestroy(self); + + av_log(NULL, AV_LOG_DEBUG, "FSPlayer dealloc\n"); } - (void)setShouldAutoplay:(BOOL)shouldAutoplay @@ -508,7 +525,13 @@ - (void)shutdown FSPlayerSafeDestroy(self); // FSPlayerSafeDestroy会异步调用ijkmp_stop,这里需要及时更新下 - [self updateAndNotifyPlaybackScheduleWithState:MP_STATE_STOPPED]; + if ([NSThread isMainThread]) { + [self updateAndNotifyPlaybackScheduleWithState:MP_STATE_STOPPED]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateAndNotifyPlaybackScheduleWithState:MP_STATE_STOPPED]; + }); + } [self didShutdown]; } @@ -1788,15 +1811,12 @@ - (FSPlayerMessage *) obtainMessage { return [_msgPool obtain]; } -inline static FSPlayer *ffplayerRetain(void *arg) { - return (__bridge_transfer FSPlayer *) arg; -} - static int media_player_msg_loop(void* arg) { @autoreleasepool { IjkMediaPlayer *mp = (IjkMediaPlayer*)arg; - __weak FSPlayer *ffpController = ffplayerRetain(ijkmp_set_weak_thiz(mp, NULL)); + FSWeakHolder *weakHolder = (__bridge FSWeakHolder *)ijkmp_get_weak_thiz(mp); + __weak FSPlayer *ffpController = weakHolder.player; while (ffpController) { @autoreleasepool { FSPlayerMessage *msg = [ffpController obtainMessage]; @@ -1812,9 +1832,6 @@ static int media_player_msg_loop(void* arg) [ffpController performSelectorOnMainThread:@selector(postEvent:) withObject:msg waitUntilDone:NO]; } } - - // retained in prepare_async, before SDL_CreateThreadEx - ijkmp_dec_ref_p(&mp); return 0; } } @@ -2063,7 +2080,7 @@ static int onInjectOnHttpEvent(FSPlayer *mpc, int type, void *data, size_t data_ static int ijkff_inject_callback(void *opaque, int message, void *data, size_t data_size) { FSWeakHolder *weakHolder = (__bridge FSWeakHolder*)opaque; - FSPlayer *mpc = weakHolder.object; + FSPlayer *mpc = weakHolder.player; if (!mpc) return 0; @@ -2096,7 +2113,7 @@ static int ijkff_inject_callback(void *opaque, int message, void *data, size_t d static int ijkff_audio_samples_callback(void *opaque, int16_t *samples, int sampleSize, int sampleRate, int channels) { FSWeakHolder *weakHolder = (__bridge FSWeakHolder*)opaque; - FSPlayer *mpc = weakHolder.object; + FSPlayer *mpc = weakHolder.player; if (!mpc) return 0; @@ -2242,7 +2259,7 @@ - (void)audioSessionInterrupt:(NSNotification *)notification int reason = [[[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey] intValue]; switch (reason) { case AVAudioSessionInterruptionTypeBegan: { - av_log(NULL, AV_LOG_DEBUG, "audioSessionInterrupt: begin"); + av_log(NULL, AV_LOG_DEBUG, "audioSessionInterrupt: begin\n"); switch (self.playbackState) { case FSPlayerPlaybackStatePaused: case FSPlayerPlaybackStateStopped: @@ -2261,7 +2278,7 @@ - (void)audioSessionInterrupt:(NSNotification *)notification break; } case AVAudioSessionInterruptionTypeEnded: { - av_log(NULL, AV_LOG_DEBUG, "audioSessionInterrupt: end"); + av_log(NULL, AV_LOG_DEBUG, "audioSessionInterrupt: end\n"); [[AVAudioSession sharedInstance] setActive:YES error:nil]; if (_playingBeforeInterruption) { [self play];