[video_player] Initialize player when size and duration become available (#4438)

diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md
index 539a552..9eb9d24 100644
--- a/packages/video_player/video_player/CHANGELOG.md
+++ b/packages/video_player/video_player/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.2.6
+
+* Initialize player when size and duration become available on iOS
+
 ## 2.2.5
 
 * Support to closed caption WebVTT format added.
diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj
index 2921ef9..d19d578 100644
--- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 46;
+	objectVersion = 50;
 	objects = {
 
 /* Begin PBXBuildFile section */
@@ -269,7 +269,7 @@
 		97C146E61CF9000F007C117D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1100;
+				LastUpgradeCheck = 1300;
 				ORGANIZATIONNAME = "The Flutter Authors";
 				TargetAttributes = {
 					97C146ED1CF9000F007C117D = {
diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/video_player/video_player/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 3f1ee95..0632b65 100644
--- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1100"
+   LastUpgradeVersion = "1300"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m
index 62d8c53..5452c11 100644
--- a/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m
+++ b/packages/video_player/video_player/example/ios/RunnerUITests/VideoPlayerUITests.m
@@ -18,33 +18,46 @@
   [self.app launch];
 }
 
-- (void)testTabs {
+- (void)testPlayVideo {
   XCUIApplication* app = self.app;
 
   XCUIElement* remoteTab = [app.otherElements
       elementMatchingPredicate:[NSPredicate predicateWithFormat:@"selected == YES"]];
-  if (![remoteTab waitForExistenceWithTimeout:30.0]) {
-    os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
-    XCTFail(@"Failed due to not able to find selected Remote tab");
-  }
+  XCTAssertTrue([remoteTab waitForExistenceWithTimeout:30.0]);
   XCTAssertTrue([remoteTab.label containsString:@"Remote"]);
 
+  XCUIElement* playButton = app.staticTexts[@"Play"];
+  XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]);
+  [playButton tap];
+
+  XCUIElement* chirpClosedCaption = app.staticTexts[@"[ Birds chirping ]"];
+  XCTAssertTrue([chirpClosedCaption waitForExistenceWithTimeout:30.0]);
+
+  XCUIElement* buzzClosedCaption = app.staticTexts[@"[ Buzzing ]"];
+  XCTAssertTrue([buzzClosedCaption waitForExistenceWithTimeout:30.0]);
+
+  XCUIElement* playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"];
+  XCTAssertTrue([playbackSpeed1x waitForExistenceWithTimeout:30.0]);
+  [playbackSpeed1x tap];
+
+  XCUIElement* playbackSpeed5xButton = app.buttons[@"5.0x"];
+  XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]);
+  [playbackSpeed5xButton tap];
+
+  XCUIElement* playbackSpeed5x = app.staticTexts[@"Playback speed\n5.0x"];
+  XCTAssertTrue([playbackSpeed5x waitForExistenceWithTimeout:30.0]);
+
+  // Cycle through tabs.
   for (NSString* tabName in @[ @"Asset", @"List example" ]) {
     NSPredicate* predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName];
     XCUIElement* unselectedTab = [app.staticTexts elementMatchingPredicate:predicate];
-    if (![unselectedTab waitForExistenceWithTimeout:30.0]) {
-      os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
-      XCTFail(@"Failed due to not able to find unselected %@ tab", tabName);
-    }
+    XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]);
     XCTAssertFalse(unselectedTab.isSelected);
     [unselectedTab tap];
 
     XCUIElement* selectedTab = [app.otherElements
         elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]];
-    if (![selectedTab waitForExistenceWithTimeout:30.0]) {
-      os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription);
-      XCTFail(@"Failed due to not able to find selected %@ tab", tabName);
-    }
+    XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]);
     XCTAssertTrue(selectedTab.isSelected);
   }
 }
diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart
index f035720..0429827 100644
--- a/packages/video_player/video_player/example/lib/main.dart
+++ b/packages/video_player/video_player/example/lib/main.dart
@@ -298,6 +298,7 @@
                       Icons.play_arrow,
                       color: Colors.white,
                       size: 100.0,
+                      semanticLabel: 'Play',
                     ),
                   ),
                 ),
diff --git a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
index f0f672d..78626eb 100644
--- a/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
+++ b/packages/video_player/video_player/ios/Classes/FLTVideoPlayerPlugin.m
@@ -52,6 +52,8 @@
 
 static void* timeRangeContext = &timeRangeContext;
 static void* statusContext = &statusContext;
+static void* presentationSizeContext = &presentationSizeContext;
+static void* durationContext = &durationContext;
 static void* playbackLikelyToKeepUpContext = &playbackLikelyToKeepUpContext;
 static void* playbackBufferEmptyContext = &playbackBufferEmptyContext;
 static void* playbackBufferFullContext = &playbackBufferFullContext;
@@ -72,6 +74,14 @@
             options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
             context:statusContext];
   [item addObserver:self
+         forKeyPath:@"presentationSize"
+            options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+            context:presentationSizeContext];
+  [item addObserver:self
+         forKeyPath:@"duration"
+            options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
+            context:durationContext];
+  [item addObserver:self
          forKeyPath:@"playbackLikelyToKeepUp"
             options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
             context:playbackLikelyToKeepUpContext];
@@ -282,10 +292,19 @@
         break;
       case AVPlayerItemStatusReadyToPlay:
         [item addOutput:_videoOutput];
-        [self sendInitialized];
+        [self setupEventSinkIfReadyToPlay];
         [self updatePlayingState];
         break;
     }
+  } else if (context == presentationSizeContext || context == durationContext) {
+    AVPlayerItem* item = (AVPlayerItem*)object;
+    if (item.status == AVPlayerItemStatusReadyToPlay) {
+      // Due to an apparent bug, when the player item is ready, it still may not have determined
+      // its presentation size or duration. When these properties are finally set, re-check if
+      // all required properties and instantiate the event sink if it is not already set up.
+      [self setupEventSinkIfReadyToPlay];
+      [self updatePlayingState];
+    }
   } else if (context == playbackLikelyToKeepUpContext) {
     if ([[_player currentItem] isPlaybackLikelyToKeepUp]) {
       [self updatePlayingState];
@@ -316,7 +335,7 @@
   _displayLink.paused = !_isPlaying;
 }
 
-- (void)sendInitialized {
+- (void)setupEventSinkIfReadyToPlay {
   if (_eventSink && !_isInitialized) {
     CGSize size = [self.player currentItem].presentationSize;
     CGFloat width = size.width;
@@ -425,7 +444,7 @@
   // This line ensures the 'initialized' event is sent when the event
   // 'AVPlayerItemStatusReadyToPlay' fires before _eventSink is set (this function
   // onListenWithArguments is called)
-  [self sendInitialized];
+  [self setupEventSinkIfReadyToPlay];
   return nil;
 }
 
diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml
index a6ee2d5..c002ed8 100644
--- a/packages/video_player/video_player/pubspec.yaml
+++ b/packages/video_player/video_player/pubspec.yaml
@@ -3,7 +3,7 @@
   widgets on Android, iOS, and web.
 repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
-version: 2.2.5
+version: 2.2.6
 
 environment:
   sdk: ">=2.14.0 <3.0.0"