Jelajahi Sumber

VLCMediaPlayer: use libdispatch instead of abusing NSThread

(cherry picked from commit 8d38df0e4dc9a5282486028057b0b091b5101245)
Felix Paul Kühne 7 tahun lalu
induk
melakukan
973e2168de
1 mengubah file dengan 52 tambahan dan 49 penghapusan
  1. 52 49
      Sources/VLCMediaPlayer.m

+ 52 - 49
Sources/VLCMediaPlayer.m

@@ -3,7 +3,7 @@
  *****************************************************************************
  * Copyright (C) 2007-2009 Pierre d'Herbemont
  * Copyright (C) 2007-2015 VLC authors and VideoLAN
- * Partial Copyright (C) 2009-2015 Felix Paul Kühne
+ * Partial Copyright (C) 2009-2017 Felix Paul Kühne
  * $Id$
  *
  * Authors: Pierre d'Herbemont <pdherbemont # videolan.org>
@@ -86,6 +86,7 @@ NSString * VLCMediaPlayerStateToString(VLCMediaPlayerState state)
 
 - (void)registerObservers;
 - (void)unregisterObservers;
+- (dispatch_queue_t)libVLCBackgroundQueue;
 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime;
 - (void)mediaPlayerPositionChanged:(NSNumber *)newTime;
 - (void)mediaPlayerStateChanged:(NSNumber *)newState;
@@ -220,6 +221,7 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
     libvlc_equalizer_t *_equalizerInstance;     ///< The equalizer controller
     BOOL _equalizerEnabled;                     ///< Equalizer state
     libvlc_video_viewpoint_t *_viewpoint;       ///< Current viewpoint of the media
+    dispatch_queue_t _libVLCBackgroundQueue;    ///< Background dispatch queue to call libvlc
 }
 @end
 
@@ -258,6 +260,7 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
         _cachedRemainingTime = [VLCTime nullTime];
         _position = 0.0f;
         _cachedState = VLCMediaPlayerStateStopped;
+        _libVLCBackgroundQueue = [self libVLCBackgroundQueue];
 
         _privateLibrary = library;
         libvlc_retain([_privateLibrary instance]);
@@ -298,7 +301,9 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
 
 - (void)dealloc
 {
-    NSAssert(libvlc_media_player_get_state(_playerInstance) == libvlc_Stopped || libvlc_media_player_get_state(_playerInstance) == libvlc_NothingSpecial, @"You released the media player before ensuring that it is stopped");
+    NSAssert(libvlc_media_player_get_state(_playerInstance) == libvlc_Stopped ||
+             libvlc_media_player_get_state(_playerInstance) == libvlc_NothingSpecial,
+             @"You released the media player before ensuring that it is stopped");
 
     [self unregisterObservers];
     [[VLCEventManager sharedManager] cancelCallToObject:self];
@@ -321,6 +326,7 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
         libvlc_free(_viewpoint);
 
     libvlc_media_player_release(_playerInstance);
+
     if (_privateLibrary != [VLCLibrary sharedLibrary])
         libvlc_release(_privateLibrary.instance);
 }
@@ -341,7 +347,12 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
 {
     // Make sure that this instance has been associated with the drawing canvas.
     _drawable = aDrawable;
-    libvlc_media_player_set_nsobject(_playerInstance, (__bridge void *)(_drawable));
+
+    /* Note that ee need the caller to wait until the setter succeeded.
+     * Otherwise, s/he might want to deploy the drawable while it isn’t ready yet. */
+    dispatch_sync(_libVLCBackgroundQueue, ^{
+        libvlc_media_player_set_nsobject(_playerInstance, (__bridge void *)(aDrawable));
+    });
 }
 
 - (id)drawable
@@ -1080,45 +1091,24 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
 
 - (void)play
 {
-    if ([NSThread isMainThread]) {
-        /* Hack because we create a dead lock here, when the vout is created
-         * and tries to recontact us on the main thread */
-        /* FIXME: to do this properly we need to do some locking. We may want
-         * to move that to libvlc */
-        [self performSelectorInBackground:@selector(play) withObject:nil];
-        return;
-    }
-
-    libvlc_media_player_play(_playerInstance);
+    dispatch_async(_libVLCBackgroundQueue, ^{
+        libvlc_media_player_play(_playerInstance);
+    });
 }
 
 - (void)pause
 {
-    if ([NSThread isMainThread]) {
-        /* Hack because we create a dead lock here, when the vout is stopped
-         * and tries to recontact us on the main thread */
-        /* FIXME: to do this properly we need to do some locking. We may want
-         * to move that to libvlc */
-        [self performSelectorInBackground:@selector(pause) withObject:nil];
-        return;
-    }
-
     // Pause the stream
-    libvlc_media_player_set_pause(_playerInstance, 1);
+    dispatch_async(_libVLCBackgroundQueue, ^{
+        libvlc_media_player_set_pause(_playerInstance, 1);
+    });
 }
 
 - (void)stop
 {
-    if ([NSThread isMainThread]) {
-        /* Hack because we create a dead lock here, when the vout is stopped
-         * and tries to recontact us on the main thread */
-        /* FIXME: to do this properly we need to do some locking. We may want
-         * to move that to libvlc */
-        [self performSelectorInBackground:@selector(stop) withObject:nil];
-        return;
-    }
-
-    libvlc_media_player_stop(_playerInstance);
+    dispatch_async(_libVLCBackgroundQueue, ^{
+        libvlc_media_player_stop(_playerInstance);
+    });
 }
 
 - (BOOL)updateViewpoint:(CGFloat)yaw pitch:(CGFloat)pitch roll:(CGFloat)roll fov:(CGFloat)fov absolute:(BOOL)absolute
@@ -1297,6 +1287,7 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
         _cachedRemainingTime = [VLCTime nullTime];
         _position = 0.0f;
         _cachedState = VLCMediaPlayerStateStopped;
+        _libVLCBackgroundQueue = [self libVLCBackgroundQueue];
 
         // Create a media instance, it doesn't matter what library we start off with
         // it will change depending on the media descriptor provided to the media
@@ -1321,28 +1312,32 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
 - (void)registerObservers
 {
     // Attach event observers into the media instance
-    libvlc_event_manager_t * p_em = libvlc_media_player_event_manager(_playerInstance);
+    __block libvlc_event_manager_t * p_em = libvlc_media_player_event_manager(_playerInstance);
     if (!p_em)
         return;
 
-    libvlc_event_attach(p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerStopped,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerOpening,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerBuffering,        HandleMediaInstanceStateChanged, (__bridge void *)(self));
+    /* We need the caller to wait until this block is done.
+     * The initialized object shall not be returned until the event attachments are done. */
+    dispatch_sync(_libVLCBackgroundQueue,^{
+        libvlc_event_attach(p_em, libvlc_MediaPlayerPlaying,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerPaused,           HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerEncounteredError, HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerEndReached,       HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerStopped,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerOpening,          HandleMediaInstanceStateChanged, (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerBuffering,        HandleMediaInstanceStateChanged, (__bridge void *)(self));
 
-    libvlc_event_attach(p_em, libvlc_MediaPlayerPositionChanged,  HandleMediaPositionChanged,      (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerTimeChanged,      HandleMediaTimeChanged,          (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerMediaChanged,     HandleMediaPlayerMediaChanged,   (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerPositionChanged,  HandleMediaPositionChanged,      (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerTimeChanged,      HandleMediaTimeChanged,          (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerMediaChanged,     HandleMediaPlayerMediaChanged,   (__bridge void *)(self));
 
-    libvlc_event_attach(p_em, libvlc_MediaPlayerTitleChanged,     HandleMediaTitleChanged,         (__bridge void *)(self));
-    libvlc_event_attach(p_em, libvlc_MediaPlayerChapterChanged,   HandleMediaChapterChanged,       (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerTitleChanged,     HandleMediaTitleChanged,         (__bridge void *)(self));
+        libvlc_event_attach(p_em, libvlc_MediaPlayerChapterChanged,   HandleMediaChapterChanged,       (__bridge void *)(self));
 
-#if TARGET_OS_IPHONE
-    libvlc_event_attach(p_em, libvlc_MediaPlayerSnapshotTaken,    HandleMediaPlayerSnapshot,       (__bridge void *)(self));
-#endif
+    #if TARGET_OS_IPHONE
+        libvlc_event_attach(p_em, libvlc_MediaPlayerSnapshotTaken,    HandleMediaPlayerSnapshot,       (__bridge void *)(self));
+    #endif
+    });
 }
 
 - (void)unregisterObservers
@@ -1371,6 +1366,14 @@ static void HandleMediaPlayerSnapshot(const libvlc_event_t * event, void * self)
 #endif
 }
 
+- (dispatch_queue_t)libVLCBackgroundQueue
+{
+    if (!_libVLCBackgroundQueue) {
+        _libVLCBackgroundQueue = dispatch_queue_create("libvlcQueue", DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
+    }
+    return  _libVLCBackgroundQueue;
+}
+
 - (void)mediaPlayerTimeChanged:(NSNumber *)newTime
 {
     [self willChangeValueForKey:@"time"];