├── .gitignore ├── JRVideoPlayer.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── boyun.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── boyun.xcuserdatad │ └── xcschemes │ ├── JRVideoPlayer.xcscheme │ └── xcschememanagement.plist ├── JRVideoPlayer ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist ├── ViewController.h ├── ViewController.m ├── main.m └── test.mp4 ├── JRVideoPlayerTests ├── Info.plist └── JRVideoPlayerTests.m ├── JRVideoPlayerViewController ├── JRPlayerView.h ├── JRPlayerView.m ├── JRPlayerViewController.h ├── JRPlayerViewController.m ├── JRPlayerViewController.xib └── images │ ├── backward_disable@2x.png │ ├── backward_nor@2x.png │ ├── fast_backward_disable@2x.png │ ├── fast_backward_nor@2x.png │ ├── fast_forward_disable@2x.png │ ├── fast_forward_nor@2x.png │ ├── forward_disable@2x.png │ ├── forward_nor@2x.png │ ├── pause_disable@2x.png │ ├── pause_nor@2x.png │ ├── play_disable@2x.png │ ├── play_nor@2x.png │ ├── progressThumb@2x.png │ ├── video_brightness_bg@2x.png │ ├── video_num_bg@2x.png │ ├── video_num_front@2x.png │ ├── video_tips@2x.png │ └── video_volume_bg@2x.png ├── README.md ├── iOS Simulator Screen Shot 2015_8_4_4.27.48.png └── iOS Simulator Screen Shot 2015_8_4_4.28.14.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7A7C7AFB1B70A40B008A67B9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7AFA1B70A40B008A67B9 /* main.m */; }; 11 | 7A7C7AFE1B70A40B008A67B9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7AFD1B70A40B008A67B9 /* AppDelegate.m */; }; 12 | 7A7C7B011B70A40B008A67B9 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7B001B70A40B008A67B9 /* ViewController.m */; }; 13 | 7A7C7B041B70A40B008A67B9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B021B70A40B008A67B9 /* Main.storyboard */; }; 14 | 7A7C7B061B70A40B008A67B9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B051B70A40B008A67B9 /* Images.xcassets */; }; 15 | 7A7C7B091B70A40B008A67B9 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B071B70A40B008A67B9 /* LaunchScreen.xib */; }; 16 | 7A7C7B151B70A40B008A67B9 /* JRVideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7B141B70A40B008A67B9 /* JRVideoPlayerTests.m */; }; 17 | 7A7C7B371B70A473008A67B9 /* backward_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B201B70A473008A67B9 /* backward_disable@2x.png */; }; 18 | 7A7C7B381B70A473008A67B9 /* backward_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B211B70A473008A67B9 /* backward_nor@2x.png */; }; 19 | 7A7C7B391B70A473008A67B9 /* fast_backward_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B221B70A473008A67B9 /* fast_backward_disable@2x.png */; }; 20 | 7A7C7B3A1B70A473008A67B9 /* fast_backward_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B231B70A473008A67B9 /* fast_backward_nor@2x.png */; }; 21 | 7A7C7B3B1B70A473008A67B9 /* fast_forward_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B241B70A473008A67B9 /* fast_forward_disable@2x.png */; }; 22 | 7A7C7B3C1B70A473008A67B9 /* fast_forward_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B251B70A473008A67B9 /* fast_forward_nor@2x.png */; }; 23 | 7A7C7B3D1B70A473008A67B9 /* forward_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B261B70A473008A67B9 /* forward_disable@2x.png */; }; 24 | 7A7C7B3E1B70A473008A67B9 /* forward_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B271B70A473008A67B9 /* forward_nor@2x.png */; }; 25 | 7A7C7B3F1B70A473008A67B9 /* pause_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B281B70A473008A67B9 /* pause_disable@2x.png */; }; 26 | 7A7C7B401B70A473008A67B9 /* pause_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B291B70A473008A67B9 /* pause_nor@2x.png */; }; 27 | 7A7C7B411B70A473008A67B9 /* play_disable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2A1B70A473008A67B9 /* play_disable@2x.png */; }; 28 | 7A7C7B421B70A473008A67B9 /* play_nor@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2B1B70A473008A67B9 /* play_nor@2x.png */; }; 29 | 7A7C7B431B70A473008A67B9 /* progressThumb@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2C1B70A473008A67B9 /* progressThumb@2x.png */; }; 30 | 7A7C7B441B70A473008A67B9 /* video_brightness_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2D1B70A473008A67B9 /* video_brightness_bg@2x.png */; }; 31 | 7A7C7B451B70A473008A67B9 /* video_num_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2E1B70A473008A67B9 /* video_num_bg@2x.png */; }; 32 | 7A7C7B461B70A473008A67B9 /* video_num_front@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B2F1B70A473008A67B9 /* video_num_front@2x.png */; }; 33 | 7A7C7B471B70A473008A67B9 /* video_tips@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B301B70A473008A67B9 /* video_tips@2x.png */; }; 34 | 7A7C7B481B70A473008A67B9 /* video_volume_bg@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B311B70A473008A67B9 /* video_volume_bg@2x.png */; }; 35 | 7A7C7B491B70A473008A67B9 /* JRPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7B331B70A473008A67B9 /* JRPlayerView.m */; }; 36 | 7A7C7B4A1B70A473008A67B9 /* JRPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A7C7B351B70A473008A67B9 /* JRPlayerViewController.m */; }; 37 | 7A7C7B4B1B70A473008A67B9 /* JRPlayerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B361B70A473008A67B9 /* JRPlayerViewController.xib */; }; 38 | 7A7C7B4D1B70A9A8008A67B9 /* test.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 7A7C7B4C1B70A9A8008A67B9 /* test.mp4 */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | 7A7C7B0F1B70A40B008A67B9 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 7A7C7AED1B70A40B008A67B9 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 7A7C7AF41B70A40B008A67B9; 47 | remoteInfo = JRVideoPlayer; 48 | }; 49 | /* End PBXContainerItemProxy section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 7A7C7AF51B70A40B008A67B9 /* JRVideoPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JRVideoPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 7A7C7AF91B70A40B008A67B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 7A7C7AFA1B70A40B008A67B9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 7A7C7AFC1B70A40B008A67B9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | 7A7C7AFD1B70A40B008A67B9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | 7A7C7AFF1B70A40B008A67B9 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 58 | 7A7C7B001B70A40B008A67B9 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 59 | 7A7C7B031B70A40B008A67B9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | 7A7C7B051B70A40B008A67B9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 61 | 7A7C7B081B70A40B008A67B9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 62 | 7A7C7B0E1B70A40B008A67B9 /* JRVideoPlayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JRVideoPlayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 7A7C7B131B70A40B008A67B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 7A7C7B141B70A40B008A67B9 /* JRVideoPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JRVideoPlayerTests.m; sourceTree = ""; }; 65 | 7A7C7B201B70A473008A67B9 /* backward_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backward_disable@2x.png"; sourceTree = ""; }; 66 | 7A7C7B211B70A473008A67B9 /* backward_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "backward_nor@2x.png"; sourceTree = ""; }; 67 | 7A7C7B221B70A473008A67B9 /* fast_backward_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fast_backward_disable@2x.png"; sourceTree = ""; }; 68 | 7A7C7B231B70A473008A67B9 /* fast_backward_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fast_backward_nor@2x.png"; sourceTree = ""; }; 69 | 7A7C7B241B70A473008A67B9 /* fast_forward_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fast_forward_disable@2x.png"; sourceTree = ""; }; 70 | 7A7C7B251B70A473008A67B9 /* fast_forward_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fast_forward_nor@2x.png"; sourceTree = ""; }; 71 | 7A7C7B261B70A473008A67B9 /* forward_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "forward_disable@2x.png"; sourceTree = ""; }; 72 | 7A7C7B271B70A473008A67B9 /* forward_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "forward_nor@2x.png"; sourceTree = ""; }; 73 | 7A7C7B281B70A473008A67B9 /* pause_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pause_disable@2x.png"; sourceTree = ""; }; 74 | 7A7C7B291B70A473008A67B9 /* pause_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "pause_nor@2x.png"; sourceTree = ""; }; 75 | 7A7C7B2A1B70A473008A67B9 /* play_disable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play_disable@2x.png"; sourceTree = ""; }; 76 | 7A7C7B2B1B70A473008A67B9 /* play_nor@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "play_nor@2x.png"; sourceTree = ""; }; 77 | 7A7C7B2C1B70A473008A67B9 /* progressThumb@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "progressThumb@2x.png"; sourceTree = ""; }; 78 | 7A7C7B2D1B70A473008A67B9 /* video_brightness_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_brightness_bg@2x.png"; sourceTree = ""; }; 79 | 7A7C7B2E1B70A473008A67B9 /* video_num_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_num_bg@2x.png"; sourceTree = ""; }; 80 | 7A7C7B2F1B70A473008A67B9 /* video_num_front@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_num_front@2x.png"; sourceTree = ""; }; 81 | 7A7C7B301B70A473008A67B9 /* video_tips@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_tips@2x.png"; sourceTree = ""; }; 82 | 7A7C7B311B70A473008A67B9 /* video_volume_bg@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_volume_bg@2x.png"; sourceTree = ""; }; 83 | 7A7C7B321B70A473008A67B9 /* JRPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRPlayerView.h; sourceTree = ""; }; 84 | 7A7C7B331B70A473008A67B9 /* JRPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRPlayerView.m; sourceTree = ""; }; 85 | 7A7C7B341B70A473008A67B9 /* JRPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRPlayerViewController.h; sourceTree = ""; }; 86 | 7A7C7B351B70A473008A67B9 /* JRPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRPlayerViewController.m; sourceTree = ""; }; 87 | 7A7C7B361B70A473008A67B9 /* JRPlayerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = JRPlayerViewController.xib; sourceTree = ""; }; 88 | 7A7C7B4C1B70A9A8008A67B9 /* test.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.mp4; sourceTree = ""; }; 89 | /* End PBXFileReference section */ 90 | 91 | /* Begin PBXFrameworksBuildPhase section */ 92 | 7A7C7AF21B70A40B008A67B9 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | 7A7C7B0B1B70A40B008A67B9 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXFrameworksBuildPhase section */ 107 | 108 | /* Begin PBXGroup section */ 109 | 7A7C7AEC1B70A40B008A67B9 = { 110 | isa = PBXGroup; 111 | children = ( 112 | 7A7C7B1E1B70A473008A67B9 /* JRVideoPlayerViewController */, 113 | 7A7C7AF71B70A40B008A67B9 /* JRVideoPlayer */, 114 | 7A7C7B111B70A40B008A67B9 /* JRVideoPlayerTests */, 115 | 7A7C7AF61B70A40B008A67B9 /* Products */, 116 | ); 117 | sourceTree = ""; 118 | }; 119 | 7A7C7AF61B70A40B008A67B9 /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 7A7C7AF51B70A40B008A67B9 /* JRVideoPlayer.app */, 123 | 7A7C7B0E1B70A40B008A67B9 /* JRVideoPlayerTests.xctest */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | 7A7C7AF71B70A40B008A67B9 /* JRVideoPlayer */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 7A7C7B4C1B70A9A8008A67B9 /* test.mp4 */, 132 | 7A7C7AFC1B70A40B008A67B9 /* AppDelegate.h */, 133 | 7A7C7AFD1B70A40B008A67B9 /* AppDelegate.m */, 134 | 7A7C7AFF1B70A40B008A67B9 /* ViewController.h */, 135 | 7A7C7B001B70A40B008A67B9 /* ViewController.m */, 136 | 7A7C7B021B70A40B008A67B9 /* Main.storyboard */, 137 | 7A7C7B051B70A40B008A67B9 /* Images.xcassets */, 138 | 7A7C7B071B70A40B008A67B9 /* LaunchScreen.xib */, 139 | 7A7C7AF81B70A40B008A67B9 /* Supporting Files */, 140 | ); 141 | path = JRVideoPlayer; 142 | sourceTree = ""; 143 | }; 144 | 7A7C7AF81B70A40B008A67B9 /* Supporting Files */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 7A7C7AF91B70A40B008A67B9 /* Info.plist */, 148 | 7A7C7AFA1B70A40B008A67B9 /* main.m */, 149 | ); 150 | name = "Supporting Files"; 151 | sourceTree = ""; 152 | }; 153 | 7A7C7B111B70A40B008A67B9 /* JRVideoPlayerTests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 7A7C7B141B70A40B008A67B9 /* JRVideoPlayerTests.m */, 157 | 7A7C7B121B70A40B008A67B9 /* Supporting Files */, 158 | ); 159 | path = JRVideoPlayerTests; 160 | sourceTree = ""; 161 | }; 162 | 7A7C7B121B70A40B008A67B9 /* Supporting Files */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 7A7C7B131B70A40B008A67B9 /* Info.plist */, 166 | ); 167 | name = "Supporting Files"; 168 | sourceTree = ""; 169 | }; 170 | 7A7C7B1E1B70A473008A67B9 /* JRVideoPlayerViewController */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 7A7C7B1F1B70A473008A67B9 /* images */, 174 | 7A7C7B321B70A473008A67B9 /* JRPlayerView.h */, 175 | 7A7C7B331B70A473008A67B9 /* JRPlayerView.m */, 176 | 7A7C7B341B70A473008A67B9 /* JRPlayerViewController.h */, 177 | 7A7C7B351B70A473008A67B9 /* JRPlayerViewController.m */, 178 | 7A7C7B361B70A473008A67B9 /* JRPlayerViewController.xib */, 179 | ); 180 | path = JRVideoPlayerViewController; 181 | sourceTree = ""; 182 | }; 183 | 7A7C7B1F1B70A473008A67B9 /* images */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 7A7C7B201B70A473008A67B9 /* backward_disable@2x.png */, 187 | 7A7C7B211B70A473008A67B9 /* backward_nor@2x.png */, 188 | 7A7C7B221B70A473008A67B9 /* fast_backward_disable@2x.png */, 189 | 7A7C7B231B70A473008A67B9 /* fast_backward_nor@2x.png */, 190 | 7A7C7B241B70A473008A67B9 /* fast_forward_disable@2x.png */, 191 | 7A7C7B251B70A473008A67B9 /* fast_forward_nor@2x.png */, 192 | 7A7C7B261B70A473008A67B9 /* forward_disable@2x.png */, 193 | 7A7C7B271B70A473008A67B9 /* forward_nor@2x.png */, 194 | 7A7C7B281B70A473008A67B9 /* pause_disable@2x.png */, 195 | 7A7C7B291B70A473008A67B9 /* pause_nor@2x.png */, 196 | 7A7C7B2A1B70A473008A67B9 /* play_disable@2x.png */, 197 | 7A7C7B2B1B70A473008A67B9 /* play_nor@2x.png */, 198 | 7A7C7B2C1B70A473008A67B9 /* progressThumb@2x.png */, 199 | 7A7C7B2D1B70A473008A67B9 /* video_brightness_bg@2x.png */, 200 | 7A7C7B2E1B70A473008A67B9 /* video_num_bg@2x.png */, 201 | 7A7C7B2F1B70A473008A67B9 /* video_num_front@2x.png */, 202 | 7A7C7B301B70A473008A67B9 /* video_tips@2x.png */, 203 | 7A7C7B311B70A473008A67B9 /* video_volume_bg@2x.png */, 204 | ); 205 | path = images; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXGroup section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 7A7C7AF41B70A40B008A67B9 /* JRVideoPlayer */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 7A7C7B181B70A40B008A67B9 /* Build configuration list for PBXNativeTarget "JRVideoPlayer" */; 214 | buildPhases = ( 215 | 7A7C7AF11B70A40B008A67B9 /* Sources */, 216 | 7A7C7AF21B70A40B008A67B9 /* Frameworks */, 217 | 7A7C7AF31B70A40B008A67B9 /* Resources */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ); 223 | name = JRVideoPlayer; 224 | productName = JRVideoPlayer; 225 | productReference = 7A7C7AF51B70A40B008A67B9 /* JRVideoPlayer.app */; 226 | productType = "com.apple.product-type.application"; 227 | }; 228 | 7A7C7B0D1B70A40B008A67B9 /* JRVideoPlayerTests */ = { 229 | isa = PBXNativeTarget; 230 | buildConfigurationList = 7A7C7B1B1B70A40B008A67B9 /* Build configuration list for PBXNativeTarget "JRVideoPlayerTests" */; 231 | buildPhases = ( 232 | 7A7C7B0A1B70A40B008A67B9 /* Sources */, 233 | 7A7C7B0B1B70A40B008A67B9 /* Frameworks */, 234 | 7A7C7B0C1B70A40B008A67B9 /* Resources */, 235 | ); 236 | buildRules = ( 237 | ); 238 | dependencies = ( 239 | 7A7C7B101B70A40B008A67B9 /* PBXTargetDependency */, 240 | ); 241 | name = JRVideoPlayerTests; 242 | productName = JRVideoPlayerTests; 243 | productReference = 7A7C7B0E1B70A40B008A67B9 /* JRVideoPlayerTests.xctest */; 244 | productType = "com.apple.product-type.bundle.unit-test"; 245 | }; 246 | /* End PBXNativeTarget section */ 247 | 248 | /* Begin PBXProject section */ 249 | 7A7C7AED1B70A40B008A67B9 /* Project object */ = { 250 | isa = PBXProject; 251 | attributes = { 252 | LastUpgradeCheck = 0640; 253 | ORGANIZATIONNAME = Zhan; 254 | TargetAttributes = { 255 | 7A7C7AF41B70A40B008A67B9 = { 256 | CreatedOnToolsVersion = 6.4; 257 | }; 258 | 7A7C7B0D1B70A40B008A67B9 = { 259 | CreatedOnToolsVersion = 6.4; 260 | TestTargetID = 7A7C7AF41B70A40B008A67B9; 261 | }; 262 | }; 263 | }; 264 | buildConfigurationList = 7A7C7AF01B70A40B008A67B9 /* Build configuration list for PBXProject "JRVideoPlayer" */; 265 | compatibilityVersion = "Xcode 3.2"; 266 | developmentRegion = English; 267 | hasScannedForEncodings = 0; 268 | knownRegions = ( 269 | en, 270 | Base, 271 | ); 272 | mainGroup = 7A7C7AEC1B70A40B008A67B9; 273 | productRefGroup = 7A7C7AF61B70A40B008A67B9 /* Products */; 274 | projectDirPath = ""; 275 | projectRoot = ""; 276 | targets = ( 277 | 7A7C7AF41B70A40B008A67B9 /* JRVideoPlayer */, 278 | 7A7C7B0D1B70A40B008A67B9 /* JRVideoPlayerTests */, 279 | ); 280 | }; 281 | /* End PBXProject section */ 282 | 283 | /* Begin PBXResourcesBuildPhase section */ 284 | 7A7C7AF31B70A40B008A67B9 /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 7A7C7B391B70A473008A67B9 /* fast_backward_disable@2x.png in Resources */, 289 | 7A7C7B401B70A473008A67B9 /* pause_nor@2x.png in Resources */, 290 | 7A7C7B041B70A40B008A67B9 /* Main.storyboard in Resources */, 291 | 7A7C7B461B70A473008A67B9 /* video_num_front@2x.png in Resources */, 292 | 7A7C7B471B70A473008A67B9 /* video_tips@2x.png in Resources */, 293 | 7A7C7B4B1B70A473008A67B9 /* JRPlayerViewController.xib in Resources */, 294 | 7A7C7B091B70A40B008A67B9 /* LaunchScreen.xib in Resources */, 295 | 7A7C7B4D1B70A9A8008A67B9 /* test.mp4 in Resources */, 296 | 7A7C7B371B70A473008A67B9 /* backward_disable@2x.png in Resources */, 297 | 7A7C7B411B70A473008A67B9 /* play_disable@2x.png in Resources */, 298 | 7A7C7B431B70A473008A67B9 /* progressThumb@2x.png in Resources */, 299 | 7A7C7B3C1B70A473008A67B9 /* fast_forward_nor@2x.png in Resources */, 300 | 7A7C7B441B70A473008A67B9 /* video_brightness_bg@2x.png in Resources */, 301 | 7A7C7B3E1B70A473008A67B9 /* forward_nor@2x.png in Resources */, 302 | 7A7C7B481B70A473008A67B9 /* video_volume_bg@2x.png in Resources */, 303 | 7A7C7B3A1B70A473008A67B9 /* fast_backward_nor@2x.png in Resources */, 304 | 7A7C7B3B1B70A473008A67B9 /* fast_forward_disable@2x.png in Resources */, 305 | 7A7C7B451B70A473008A67B9 /* video_num_bg@2x.png in Resources */, 306 | 7A7C7B421B70A473008A67B9 /* play_nor@2x.png in Resources */, 307 | 7A7C7B061B70A40B008A67B9 /* Images.xcassets in Resources */, 308 | 7A7C7B381B70A473008A67B9 /* backward_nor@2x.png in Resources */, 309 | 7A7C7B3F1B70A473008A67B9 /* pause_disable@2x.png in Resources */, 310 | 7A7C7B3D1B70A473008A67B9 /* forward_disable@2x.png in Resources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 7A7C7B0C1B70A40B008A67B9 /* Resources */ = { 315 | isa = PBXResourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | /* End PBXResourcesBuildPhase section */ 322 | 323 | /* Begin PBXSourcesBuildPhase section */ 324 | 7A7C7AF11B70A40B008A67B9 /* Sources */ = { 325 | isa = PBXSourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | 7A7C7B011B70A40B008A67B9 /* ViewController.m in Sources */, 329 | 7A7C7B4A1B70A473008A67B9 /* JRPlayerViewController.m in Sources */, 330 | 7A7C7B491B70A473008A67B9 /* JRPlayerView.m in Sources */, 331 | 7A7C7AFE1B70A40B008A67B9 /* AppDelegate.m in Sources */, 332 | 7A7C7AFB1B70A40B008A67B9 /* main.m in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 7A7C7B0A1B70A40B008A67B9 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 7A7C7B151B70A40B008A67B9 /* JRVideoPlayerTests.m in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXSourcesBuildPhase section */ 345 | 346 | /* Begin PBXTargetDependency section */ 347 | 7A7C7B101B70A40B008A67B9 /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = 7A7C7AF41B70A40B008A67B9 /* JRVideoPlayer */; 350 | targetProxy = 7A7C7B0F1B70A40B008A67B9 /* PBXContainerItemProxy */; 351 | }; 352 | /* End PBXTargetDependency section */ 353 | 354 | /* Begin PBXVariantGroup section */ 355 | 7A7C7B021B70A40B008A67B9 /* Main.storyboard */ = { 356 | isa = PBXVariantGroup; 357 | children = ( 358 | 7A7C7B031B70A40B008A67B9 /* Base */, 359 | ); 360 | name = Main.storyboard; 361 | sourceTree = ""; 362 | }; 363 | 7A7C7B071B70A40B008A67B9 /* LaunchScreen.xib */ = { 364 | isa = PBXVariantGroup; 365 | children = ( 366 | 7A7C7B081B70A40B008A67B9 /* Base */, 367 | ); 368 | name = LaunchScreen.xib; 369 | sourceTree = ""; 370 | }; 371 | /* End PBXVariantGroup section */ 372 | 373 | /* Begin XCBuildConfiguration section */ 374 | 7A7C7B161B70A40B008A67B9 /* Debug */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BOOL_CONVERSION = YES; 383 | CLANG_WARN_CONSTANT_CONVERSION = YES; 384 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 385 | CLANG_WARN_EMPTY_BODY = YES; 386 | CLANG_WARN_ENUM_CONVERSION = YES; 387 | CLANG_WARN_INT_CONVERSION = YES; 388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 389 | CLANG_WARN_UNREACHABLE_CODE = YES; 390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 391 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 392 | COPY_PHASE_STRIP = NO; 393 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 394 | ENABLE_STRICT_OBJC_MSGSEND = YES; 395 | GCC_C_LANGUAGE_STANDARD = gnu99; 396 | GCC_DYNAMIC_NO_PIC = NO; 397 | GCC_NO_COMMON_BLOCKS = YES; 398 | GCC_OPTIMIZATION_LEVEL = 0; 399 | GCC_PREPROCESSOR_DEFINITIONS = ( 400 | "DEBUG=1", 401 | "$(inherited)", 402 | ); 403 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 404 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 405 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 406 | GCC_WARN_UNDECLARED_SELECTOR = YES; 407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 408 | GCC_WARN_UNUSED_FUNCTION = YES; 409 | GCC_WARN_UNUSED_VARIABLE = YES; 410 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 411 | MTL_ENABLE_DEBUG_INFO = YES; 412 | ONLY_ACTIVE_ARCH = YES; 413 | SDKROOT = iphoneos; 414 | }; 415 | name = Debug; 416 | }; 417 | 7A7C7B171B70A40B008A67B9 /* Release */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 428 | CLANG_WARN_EMPTY_BODY = YES; 429 | CLANG_WARN_ENUM_CONVERSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 432 | CLANG_WARN_UNREACHABLE_CODE = YES; 433 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 434 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 435 | COPY_PHASE_STRIP = NO; 436 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 437 | ENABLE_NS_ASSERTIONS = NO; 438 | ENABLE_STRICT_OBJC_MSGSEND = YES; 439 | GCC_C_LANGUAGE_STANDARD = gnu99; 440 | GCC_NO_COMMON_BLOCKS = YES; 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 448 | MTL_ENABLE_DEBUG_INFO = NO; 449 | SDKROOT = iphoneos; 450 | VALIDATE_PRODUCT = YES; 451 | }; 452 | name = Release; 453 | }; 454 | 7A7C7B191B70A40B008A67B9 /* Debug */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 458 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 459 | CODE_SIGN_IDENTITY = ""; 460 | INFOPLIST_FILE = JRVideoPlayer/Info.plist; 461 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 462 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | PROVISIONING_PROFILE = ""; 465 | }; 466 | name = Debug; 467 | }; 468 | 7A7C7B1A1B70A40B008A67B9 /* Release */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 472 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 473 | CODE_SIGN_IDENTITY = ""; 474 | INFOPLIST_FILE = JRVideoPlayer/Info.plist; 475 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 477 | PRODUCT_NAME = "$(TARGET_NAME)"; 478 | PROVISIONING_PROFILE = ""; 479 | }; 480 | name = Release; 481 | }; 482 | 7A7C7B1C1B70A40B008A67B9 /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | BUNDLE_LOADER = "$(TEST_HOST)"; 486 | FRAMEWORK_SEARCH_PATHS = ( 487 | "$(SDKROOT)/Developer/Library/Frameworks", 488 | "$(inherited)", 489 | ); 490 | GCC_PREPROCESSOR_DEFINITIONS = ( 491 | "DEBUG=1", 492 | "$(inherited)", 493 | ); 494 | INFOPLIST_FILE = JRVideoPlayerTests/Info.plist; 495 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JRVideoPlayer.app/JRVideoPlayer"; 498 | }; 499 | name = Debug; 500 | }; 501 | 7A7C7B1D1B70A40B008A67B9 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | BUNDLE_LOADER = "$(TEST_HOST)"; 505 | FRAMEWORK_SEARCH_PATHS = ( 506 | "$(SDKROOT)/Developer/Library/Frameworks", 507 | "$(inherited)", 508 | ); 509 | INFOPLIST_FILE = JRVideoPlayerTests/Info.plist; 510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/JRVideoPlayer.app/JRVideoPlayer"; 513 | }; 514 | name = Release; 515 | }; 516 | /* End XCBuildConfiguration section */ 517 | 518 | /* Begin XCConfigurationList section */ 519 | 7A7C7AF01B70A40B008A67B9 /* Build configuration list for PBXProject "JRVideoPlayer" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | 7A7C7B161B70A40B008A67B9 /* Debug */, 523 | 7A7C7B171B70A40B008A67B9 /* Release */, 524 | ); 525 | defaultConfigurationIsVisible = 0; 526 | defaultConfigurationName = Release; 527 | }; 528 | 7A7C7B181B70A40B008A67B9 /* Build configuration list for PBXNativeTarget "JRVideoPlayer" */ = { 529 | isa = XCConfigurationList; 530 | buildConfigurations = ( 531 | 7A7C7B191B70A40B008A67B9 /* Debug */, 532 | 7A7C7B1A1B70A40B008A67B9 /* Release */, 533 | ); 534 | defaultConfigurationIsVisible = 0; 535 | defaultConfigurationName = Release; 536 | }; 537 | 7A7C7B1B1B70A40B008A67B9 /* Build configuration list for PBXNativeTarget "JRVideoPlayerTests" */ = { 538 | isa = XCConfigurationList; 539 | buildConfigurations = ( 540 | 7A7C7B1C1B70A40B008A67B9 /* Debug */, 541 | 7A7C7B1D1B70A40B008A67B9 /* Release */, 542 | ); 543 | defaultConfigurationIsVisible = 0; 544 | defaultConfigurationName = Release; 545 | }; 546 | /* End XCConfigurationList section */ 547 | }; 548 | rootObject = 7A7C7AED1B70A40B008A67B9 /* Project object */; 549 | } 550 | -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/project.xcworkspace/xcuserdata/boyun.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayer.xcodeproj/project.xcworkspace/xcuserdata/boyun.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/project.xcworkspace/xcuserdata/boyun.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/xcuserdata/boyun.xcuserdatad/xcschemes/JRVideoPlayer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /JRVideoPlayer.xcodeproj/xcuserdata/boyun.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JRVideoPlayer.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 7A7C7AF41B70A40B008A67B9 16 | 17 | primary 18 | 19 | 20 | 7A7C7B0D1B70A40B008A67B9 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /JRVideoPlayer/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /JRVideoPlayer/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /JRVideoPlayer/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /JRVideoPlayer/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /JRVideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /JRVideoPlayer/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "minimum-system-version" : "7.0", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "orientation" : "portrait", 11 | "idiom" : "iphone", 12 | "minimum-system-version" : "7.0", 13 | "subtype" : "retina4", 14 | "scale" : "2x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /JRVideoPlayer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.zhanjr.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /JRVideoPlayer/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /JRVideoPlayer/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | 10 | /** 说明 11 | 将JRVideoPlayerViewController文件夹放到工程中,导入"JRPlayerViewController.h" 12 | 13 | 播放本地视频 14 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithLocalMediaURL:url]; 15 | 16 | 播放HTTPLiveStreaming视频 17 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithHTTPLiveStreamingMediaURL:url]; 18 | */ 19 | 20 | #import "ViewController.h" 21 | #import "JRPlayerViewController.h" 22 | 23 | @interface ViewController () 24 | 25 | @property (weak, nonatomic) IBOutlet UITextField *URLTextField; 26 | 27 | @end 28 | 29 | @implementation ViewController 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | // Do any additional setup after loading the view, typically from a nib. 34 | } 35 | 36 | 37 | #pragma mark - 播放视频 38 | 39 | // 本地视频 40 | - (IBAction)playLocalVideo:(id)sender { 41 | NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4"]; 42 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithLocalMediaURL:url]; 43 | playerVC.mediaTitle = @"老男孩"; 44 | [self presentViewController:playerVC animated:YES completion:nil]; 45 | } 46 | 47 | // HTTPLiveStreaming视频流 48 | - (IBAction)playHTTPLiveStreamingVideo:(id)sender { 49 | NSURL *url = [NSURL URLWithString:@"http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8"]; 50 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithHTTPLiveStreamingMediaURL:url]; 51 | playerVC.mediaTitle = @"prog_index"; 52 | [self presentViewController:playerVC animated:YES completion:nil]; 53 | } 54 | 55 | // 播放输入的地址 56 | - (IBAction)playUrl:(UIButton *)sender { 57 | if (self.URLTextField.text != nil && ![self.URLTextField.text isEqualToString:@""]) { 58 | 59 | NSString *str = [self.URLTextField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 60 | 61 | NSURL *url = [NSURL URLWithString:str]; 62 | 63 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithHTTPLiveStreamingMediaURL:url]; 64 | 65 | playerVC.mediaTitle = @"HTTP Live Streaming"; 66 | 67 | [self presentViewController:playerVC animated:YES completion:nil]; 68 | } 69 | } 70 | 71 | - (IBAction)hideKeyboard:(UITapGestureRecognizer *)sender { 72 | [self.URLTextField resignFirstResponder]; 73 | } 74 | 75 | 76 | - (void)viewWillAppear:(BOOL)animated 77 | { 78 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 79 | } 80 | 81 | 82 | #pragma mark - 83 | - (void)didReceiveMemoryWarning { 84 | [super didReceiveMemoryWarning]; 85 | // Dispose of any resources that can be recreated. 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /JRVideoPlayer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JRVideoPlayer/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayer/test.mp4 -------------------------------------------------------------------------------- /JRVideoPlayerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.zhan.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /JRVideoPlayerTests/JRVideoPlayerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JRVideoPlayerTests.m 3 | // JRVideoPlayerTests 4 | // 5 | // Created by 湛家荣 on 15/8/4. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface JRVideoPlayerTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation JRVideoPlayerTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/JRPlayerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JRPlayerView.h 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/5/8. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | @class AVPlayer; 11 | 12 | @interface JRPlayerView : UIView 13 | 14 | @property (nonatomic, strong) AVPlayer *player; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/JRPlayerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JRPlayerView.m 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/5/8. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import "JRPlayerView.h" 10 | #import 11 | 12 | @interface JRPlayerView () 13 | 14 | @end 15 | 16 | @implementation JRPlayerView 17 | 18 | // 使PlayerView的layer为AVPlayerLayer类型 19 | + (Class)layerClass { 20 | return [AVPlayerLayer class]; 21 | } 22 | 23 | - (AVPlayer *)player { 24 | return [(AVPlayerLayer *)[self layer] player]; 25 | } 26 | 27 | - (void)setPlayer:(AVPlayer *)player { 28 | [(AVPlayerLayer *)[self layer] setPlayer:player]; 29 | } 30 | 31 | - (void)dealloc 32 | { 33 | self.player = nil; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/JRPlayerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JRPlayerViewController.h 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/5/8. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JRPlayerViewController : UIViewController 12 | 13 | @property (nonatomic, strong) NSString *mediaTitle; 14 | 15 | - (instancetype)initWithHTTPLiveStreamingMediaURL:(NSURL *)url; 16 | - (instancetype)initWithLocalMediaURL:(NSURL *)url; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/JRPlayerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JRPlayerViewController.m 3 | // JRVideoPlayer 4 | // 5 | // Created by 湛家荣 on 15/5/8. 6 | // Copyright (c) 2015年 Zhan. All rights reserved. 7 | // 8 | 9 | #import "JRPlayerViewController.h" 10 | #import 11 | #import 12 | #import "JRPlayerView.h" 13 | 14 | #define OFFSET 5.0 // 快进和快退的时间跨度 15 | #define ALPHA 0.5 // headerView和bottomView的透明度 16 | 17 | static void * playerItemDurationContext = &playerItemDurationContext; 18 | static void * playerItemStatusContext = &playerItemStatusContext; 19 | static void * playerPlayingContext = &playerPlayingContext; 20 | 21 | @interface JRPlayerViewController () 22 | { 23 | CGFloat screenWidth; 24 | CGFloat screenHeight; 25 | 26 | CGFloat brightness; // 屏幕亮度 27 | CGFloat sysVolume; // 系统声音 28 | CGFloat playerProgress; // 播放进度 29 | 30 | UISlider *volumeSlider; // 改变系统声音的 MPVolumeSlider (UISlider的子类) 31 | 32 | // 手势初始X和Y坐标 33 | CGFloat beginTouchX; 34 | CGFloat beginTouchY; 35 | // 手势相对于初始X和Y坐标的偏移量 36 | CGFloat offsetX; 37 | CGFloat offsetY; 38 | } 39 | 40 | @property (nonatomic, strong) AVPlayer *player; // 播放器 41 | @property (nonatomic, strong) AVPlayerItem *playerItem; 42 | @property (nonatomic, strong) NSURL *mediaURL; // 视频资源的url 43 | 44 | @property (nonatomic, assign) BOOL playing; // 是否正在播放 45 | @property (nonatomic, assign) BOOL canPlay; // 是否可以播放 46 | 47 | @property (nonatomic, assign) CMTime duration; // 视频总时间 48 | 49 | @property (nonatomic, strong) id timeObserver; 50 | 51 | @property (weak, nonatomic) IBOutlet JRPlayerView *playerView; 52 | 53 | @property (weak, nonatomic) IBOutlet UIView *headerView; 54 | @property (weak, nonatomic) IBOutlet UIView *bottomView; 55 | 56 | @property (weak, nonatomic) IBOutlet UIButton *backButton; // 返回 57 | @property (weak, nonatomic) IBOutlet UILabel *mediaTitleLabel; // 视频标题 58 | 59 | @property (weak, nonatomic) IBOutlet UIButton *backwardButton; // 上一个 60 | @property (weak, nonatomic) IBOutlet UIButton *fastBackwardButton; // 快退 61 | @property (weak, nonatomic) IBOutlet UIButton *playButton; // 播放 62 | @property (weak, nonatomic) IBOutlet UIButton *fastForwardButton; // 快进 63 | @property (weak, nonatomic) IBOutlet UIButton *forwardButton; // 下一个 64 | 65 | @property (weak, nonatomic) IBOutlet UILabel *currentTimeLabel; // 当前播放的时间 66 | @property (weak, nonatomic) IBOutlet UILabel *remainTimeLabel; // 剩余时间 67 | @property (weak, nonatomic) IBOutlet UISlider *progressView; // 播放进度 68 | @property (weak, nonatomic) IBOutlet UILabel *persentLabel; // 亮度或进度的百分比 69 | 70 | @end 71 | 72 | @implementation JRPlayerViewController 73 | 74 | - (instancetype)initWithLocalMediaURL:(NSURL *)url 75 | { 76 | if (self = [super init]) { 77 | self.mediaURL = url; 78 | [self createLocalMediaPlayerItem]; 79 | } 80 | return self; 81 | } 82 | 83 | - (instancetype)initWithHTTPLiveStreamingMediaURL:(NSURL *)url 84 | { 85 | if (self = [super init]) { 86 | self.mediaURL = url; 87 | [self createHLSPlayerItem]; 88 | } 89 | return self; 90 | } 91 | 92 | 93 | - (void)viewDidLoad { 94 | [super viewDidLoad]; 95 | // Do any additional setup after loading the view. 96 | self.view.backgroundColor = [UIColor whiteColor]; 97 | 98 | [self hidePersentLabel]; 99 | 100 | screenWidth = [[UIScreen mainScreen] bounds].size.width; 101 | screenHeight = [[UIScreen mainScreen] bounds].size.height; 102 | 103 | if (screenHeight > screenWidth) { // 让宽度比高度大,即横屏的宽高 104 | CGFloat tmp = screenWidth; 105 | screenWidth = screenHeight; 106 | screenHeight = tmp; 107 | } 108 | 109 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { 110 | // iOS 7 以上 111 | [self prefersStatusBarHidden]; 112 | [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)]; 113 | } 114 | 115 | [self.progressView setMinimumTrackImage:[[UIImage imageNamed:@"video_num_front.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5) resizingMode:UIImageResizingModeStretch] forState:UIControlStateNormal]; 116 | [self.progressView setMaximumTrackImage:[[UIImage imageNamed:@"video_num_bg.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5) resizingMode:UIImageResizingModeStretch] forState:UIControlStateNormal]; 117 | [self.progressView setThumbImage:[UIImage imageNamed:@"progressThumb.png"] forState:UIControlStateNormal]; 118 | 119 | // KVO观察self.playing属性的变化以改变playButton的状态 120 | [self addObserver:self forKeyPath:@"playing" options:NSKeyValueObservingOptionNew context:playerPlayingContext]; 121 | 122 | [self createPlayer];// 创建播放器 123 | 124 | // 监控 app 活动状态,打电话/锁屏 时暂停播放 125 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; 126 | 127 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; 128 | } 129 | 130 | - (void)hidePersentLabel { 131 | [self.persentLabel setHidden:YES]; 132 | } 133 | 134 | - (void)showPersentLabel { 135 | [self.persentLabel setHidden:NO]; 136 | } 137 | 138 | - (void)appWillResignActive:(NSNotification *)aNotification { 139 | [self.player pause]; 140 | self.playing = NO; 141 | } 142 | 143 | - (void)appDidBecomActive:(NSNotification *)aNotification { 144 | // 145 | } 146 | 147 | #pragma mark - 创建播放器 148 | 149 | - (void)createPlayer { 150 | self.mediaTitleLabel.text = self.mediaTitle; 151 | 152 | // 1.控制器初始化时创建playerItem对象 153 | 154 | // 2.观察self.currentItem.status属性变化,变为AVPlayerItemStatusReadyToPlay时就可以播放了 155 | [self addObserver:self forKeyPath:@"playerItem.status" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:playerItemStatusContext]; 156 | // 监听播放到最后的通知 157 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 158 | 159 | // 3.playerItem关联创建player 160 | self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; 161 | 162 | // 4.player关联创建playerView 163 | [self.playerView setPlayer:self.player]; 164 | 165 | [self.playerView.layer setBackgroundColor:[UIColor blackColor].CGColor]; 166 | } 167 | 168 | - (void)createLocalMediaPlayerItem { 169 | // 如果是本地视频,创建AVURLAsset对象 170 | AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.mediaURL options:nil]; 171 | 172 | self.playerItem = [[AVPlayerItem alloc] initWithAsset:asset]; 173 | } 174 | 175 | - (void)createHLSPlayerItem { 176 | // HTTPLiveStreaming视频流不能直接创建AVAsset,直接从url创建playerItem对象 177 | // When you associate the player item with a player, it starts to become ready to play. When it is ready to play, the player item creates the AVAsset and AVAssetTrack instances, which you can use to inspect the contents of the live stream. 178 | self.playerItem = [AVPlayerItem playerItemWithURL:self.mediaURL]; 179 | } 180 | 181 | #pragma mark 返回 182 | - (IBAction)backButtonClick:(UIButton *)sender { 183 | [self.player pause]; 184 | [self dismissViewControllerAnimated:NO completion:nil]; 185 | } 186 | 187 | #pragma mark 播放到最后时 188 | - (void)playerItemDidPlayToEnd:(NSNotification *)aNotification { 189 | [self.playerItem seekToTime:kCMTimeZero]; 190 | self.playing = NO; 191 | } 192 | 193 | #pragma mark 上一个 194 | - (IBAction)backward:(UIButton *)sender { 195 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 196 | 197 | NSLog(@"上一个"); 198 | 199 | [self delayHideHeaderViewAndBottomView]; 200 | } 201 | 202 | #pragma mark 快退 203 | - (IBAction)fastBackward:(UIButton *)sender { 204 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 205 | 206 | [self progressAdd:-OFFSET]; 207 | 208 | [self delayHideHeaderViewAndBottomView]; 209 | } 210 | 211 | #pragma mark 播放 212 | - (IBAction)play:(id)sender { 213 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 214 | 215 | if (!self.playing) { 216 | [self.player play]; 217 | 218 | self.playing = YES; // KVO观察playing属性的变化 219 | } else { 220 | [self.player pause]; 221 | 222 | self.playing = NO; 223 | } 224 | 225 | [self delayHideHeaderViewAndBottomView]; 226 | } 227 | 228 | #pragma mark 快进 229 | - (IBAction)fastForward:(UIButton *)sender { 230 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 231 | 232 | [self progressAdd:OFFSET]; 233 | 234 | [self delayHideHeaderViewAndBottomView]; 235 | } 236 | 237 | #pragma mark 下一个 238 | - (IBAction)forward:(UIButton *)sender { 239 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 240 | 241 | NSLog(@"下一个"); 242 | 243 | [self delayHideHeaderViewAndBottomView]; 244 | } 245 | 246 | 247 | - (IBAction)sliderTapGesture:(UITapGestureRecognizer *)sender { 248 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 249 | 250 | CGFloat tapX = [sender locationInView:sender.view].x; 251 | CGFloat sliderWidth = sender.view.bounds.size.width; 252 | 253 | Float64 totalSeconds = CMTimeGetSeconds(self.duration); // 总时间 254 | CMTime dstTime = CMTimeMakeWithSeconds(totalSeconds * (tapX / sliderWidth), self.duration.timescale); 255 | 256 | [self seekToCMTime:dstTime progress:self.progressView.value]; 257 | 258 | [self delayHideHeaderViewAndBottomView]; 259 | } 260 | 261 | 262 | - (void)progressAdd:(CGFloat)step { 263 | // 如果正在播放先暂停播放(但是不改变_playing的值为NO,因为快进或快退完成后要根据_playing来判断是否要继续播放),再进行播放定位 264 | if (_playing) { 265 | [self.player pause]; 266 | } 267 | 268 | Float64 currentSecond = CMTimeGetSeconds(self.player.currentTime); // 当前秒 269 | Float64 totalSeconds = CMTimeGetSeconds(self.duration); // 总时间 270 | 271 | CMTime dstTime; // 目标时间 272 | 273 | if (currentSecond + step >= totalSeconds) { 274 | dstTime = CMTimeSubtract(self.duration, CMTimeMakeWithSeconds(1, self.duration.timescale)); 275 | self.progressView.value = dstTime.value / self.duration.value; 276 | } else if (currentSecond + step < 0.0) { 277 | dstTime = kCMTimeZero; 278 | self.progressView.value = 0.0; 279 | } else { 280 | dstTime = CMTimeMakeWithSeconds(currentSecond + step, self.player.currentTime.timescale); 281 | self.progressView.value += step / CMTimeGetSeconds(self.duration); 282 | } 283 | 284 | [self seekToCMTime:dstTime progress:self.progressView.value]; 285 | } 286 | 287 | // 调整播放点 288 | - (void)seekToCMTime:(CMTime)time progress:(CGFloat)progress{ 289 | [self cancelPerformSelector:@selector(hidePersentLabel)]; 290 | [self showPersentLabel]; 291 | 292 | self.persentLabel.text = [NSString stringWithFormat:@"进度: %.2f%%", progress * 100]; 293 | 294 | // 如果正在播放先暂停播放(但是不改变_playing的值为NO,因为拖动进度完成后要根据_playing来判断是否要继续播放),再根据进度条表示的值进行播放定位 295 | if (_playing) { 296 | [self.player pause]; 297 | } 298 | 299 | [self.player seekToTime:time completionHandler:^(BOOL finished) { 300 | if (_playing) { 301 | // 如果拖动前正在播放,拖动后也要在播放状态 302 | [self.player play]; 303 | } 304 | }]; 305 | 306 | [self performSelector:@selector(hidePersentLabel) withObject:nil afterDelay:1.0f]; 307 | } 308 | 309 | #pragma mark - 拖动进度条改变播放点(playhead) 310 | // valueChanged 311 | - (IBAction)slidingProgress:(UISlider *)slider { 312 | 313 | // 取消调用hideHeaderViewAndBottomView方法,不隐藏 314 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 315 | 316 | Float64 totalSeconds = CMTimeGetSeconds(self.duration); 317 | 318 | CMTime time = CMTimeMakeWithSeconds(totalSeconds * slider.value, self.duration.timescale); 319 | 320 | [self seekToCMTime:time progress:slider.value]; 321 | } 322 | 323 | // touchUpInside/touchUpOutside 324 | - (IBAction)slidingEnded:(UISlider *)sender { 325 | // 拖动手势取消后延迟调用hideHeaderViewAndBottomView 326 | [self delayHideHeaderViewAndBottomView]; 327 | } 328 | 329 | 330 | 331 | #pragma mark 根据CMTime生成一个时间字符串 332 | - (NSString *)timeStringWithCMTime:(CMTime)time { 333 | Float64 seconds = time.value / time.timescale; 334 | // 把seconds当作时间戳得到一个date 335 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:seconds]; 336 | 337 | // 格林威治标准时间 338 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 339 | [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 340 | 341 | // 设置时间显示格式 342 | [formatter setDateFormat:(seconds / 3600 >= 1) ? @"h:mm:ss" : @"mm:ss"]; 343 | 344 | // 返回这个date的字符串形式 345 | return [formatter stringFromDate:date]; 346 | } 347 | 348 | 349 | #pragma mark - KVO 350 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 351 | { 352 | if (context == playerItemStatusContext) { 353 | 354 | if (self.playerItem.status == AVPlayerItemStatusReadyToPlay) { 355 | // 视频准备就绪 356 | dispatch_async(dispatch_get_main_queue(), ^{ 357 | [self readyToPlay]; 358 | }); 359 | } else { 360 | // 如果一个不能播放的视频资源加载进来会进到这里 361 | NSLog(@"视频无法播放"); 362 | // 延迟dismiss播放器视图控制器 363 | [self performSelector:@selector(delayDismissPlayerViewController) withObject:nil afterDelay:3.0f]; 364 | } 365 | 366 | } else if (context == playerPlayingContext){ 367 | 368 | dispatch_async(dispatch_get_main_queue(), ^{ 369 | if ([[change objectForKey:@"new"] intValue] == 1) { 370 | // 如果playing变为YES就显示暂停按钮 371 | [self.playButton setImage:[UIImage imageNamed:@"pause_nor.png"] forState:UIControlStateNormal]; 372 | } else { 373 | // 如果playing变为NO就显示播放按钮 374 | [self.playButton setImage:[UIImage imageNamed:@"play_nor.png"] forState:UIControlStateNormal]; 375 | } 376 | }); 377 | 378 | } else { 379 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 380 | } 381 | } 382 | 383 | - (void)delayDismissPlayerViewController { 384 | [self dismissViewControllerAnimated:YES completion:nil]; 385 | } 386 | 387 | #pragma mark AVPlayerItemStatusReadyToPlay 388 | - (void)readyToPlay { 389 | 390 | // 视频可以播放时才自动延迟隐藏headerView和bottomView 391 | [self delayHideHeaderViewAndBottomView]; 392 | 393 | // 可以播放 394 | self.canPlay = YES; 395 | [self.playButton setEnabled:YES]; 396 | [self.fastBackwardButton setEnabled:YES]; 397 | [self.fastForwardButton setEnabled:YES]; 398 | [self.progressView setEnabled:YES]; 399 | 400 | self.duration = self.playerItem.duration; 401 | 402 | // 未播放前剩余时间就是视频长度 403 | self.remainTimeLabel.text = [NSString stringWithFormat:@"-%@", [self timeStringWithCMTime:self.duration]]; 404 | 405 | __weak typeof(self) weakSelf = self; 406 | // 更新当前播放条目的已播时间, CMTimeMake(3, 30) == (Float64)3/30 秒 407 | self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(3, 30) queue:nil usingBlock:^(CMTime time) { 408 | // 当前播放时间 409 | weakSelf.currentTimeLabel.text = [weakSelf timeStringWithCMTime:time]; 410 | // 剩余时间 411 | NSString *text = [weakSelf timeStringWithCMTime:CMTimeSubtract(weakSelf.duration, time)]; 412 | weakSelf.remainTimeLabel.text = [NSString stringWithFormat:@"-%@", text]; 413 | 414 | // 更新进度 415 | weakSelf.progressView.value = CMTimeGetSeconds(time) / CMTimeGetSeconds(weakSelf.duration); 416 | 417 | }]; 418 | 419 | NSLog(@"状态准备就绪 -> %@", @(AVPlayerItemStatusReadyToPlay)); 420 | } 421 | 422 | #pragma mark - touch 423 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 424 | { 425 | [super touchesBegan:touches withEvent:event]; 426 | 427 | UITouch *oneTouch = [touches anyObject]; 428 | 429 | // 初始的X和Y坐标 430 | beginTouchX = [oneTouch locationInView:oneTouch.view].x; 431 | beginTouchY = [oneTouch locationInView:oneTouch.view].y; 432 | 433 | // 初始的亮度 434 | brightness = [UIScreen mainScreen].brightness; 435 | 436 | // 改变音量的控件 437 | MPVolumeView *volumeView = [[MPVolumeView alloc] init]; 438 | for (UIView *aView in [volumeView subviews]) { 439 | if ([aView.class.description isEqualToString:@"MPVolumeSlider"]) { 440 | volumeSlider = (UISlider *)aView; 441 | break; 442 | } 443 | } 444 | // 初始的音量 445 | sysVolume = volumeSlider.value; 446 | 447 | 448 | // 显示或隐藏播放工具栏 449 | if (self.headerView.alpha == 0.0) { 450 | // 隐藏状态下就显示 451 | [self showHeaderViewAndBottomView]; 452 | 453 | } else { 454 | // 显示状态下就隐藏 455 | [self hideHeaderViewAndBottomView]; 456 | 457 | // 在显示状态下点击后就隐藏,那么之前的延迟调用就要取消,不取消也不会有问题 458 | [self cancelPerformSelector:@selector(hideHeaderViewAndBottomView)]; 459 | } 460 | 461 | } 462 | 463 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 464 | { 465 | [super touchesMoved:touches withEvent:event]; 466 | 467 | UITouch *oneTouch = [touches anyObject]; 468 | 469 | // 手势相对于初始坐标的偏移量 470 | offsetX = [oneTouch locationInView:oneTouch.view].x - beginTouchX; 471 | offsetY = [oneTouch locationInView:oneTouch.view].y - beginTouchY; 472 | 473 | // 要改变的音量或亮度 474 | CGFloat delta = -offsetY / screenHeight; 475 | 476 | CGFloat touchX = [oneTouch locationInView:oneTouch.view].x; 477 | 478 | // offsetY != 0 说明有上下位移,对亮度和声音就应该有改变 479 | if (touchX < (1.0/3 * screenWidth) && offsetY != 0) { 480 | // 上下滑动改变亮度 481 | 482 | [self cancelPerformSelector:@selector(hidePersentLabel)]; 483 | [self showPersentLabel]; 484 | 485 | if (brightness + delta > 0.0 && brightness + delta < 1.0) { 486 | [[UIScreen mainScreen] setBrightness:brightness + delta]; // 设置屏幕亮度 487 | } 488 | 489 | self.persentLabel.text = [NSString stringWithFormat:@"亮度: %.2f%%", (brightness + delta) * 100]; 490 | 491 | [self performSelector:@selector(hidePersentLabel) withObject:nil afterDelay:1.0f]; 492 | 493 | } else if (touchX > (2.0/3 * screenWidth) && offsetY != 0) { 494 | // 上下滑动改变音量 495 | if (sysVolume + delta > 0.0 && sysVolume + delta < 1.0) { 496 | [volumeSlider setValue:sysVolume + delta]; // 设置音量 497 | } 498 | 499 | } else if (touchX > (1.0/3 * screenWidth) && touchX < (2.0/3 * screenWidth) && offsetX != 0) { 500 | // 中屏幕中间左右滑动改变进度 501 | 502 | if (self.canPlay) { // 如果视频可以播放才可以调整播放进度 503 | // 要改变的进度值 504 | CGFloat deltaProgress = offsetX / screenWidth; 505 | 506 | // 如果正在播放先暂停播放(但是不改变_playing的值为NO,因为拖动进度完成后要根据_playing来判断是否要继续播放),再根据进度条表示的值进行播放定位 507 | if (_playing) { 508 | [self.player pause]; 509 | } 510 | 511 | Float64 totalSeconds = CMTimeGetSeconds(self.duration); 512 | 513 | CMTime time = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.player.currentTime) + totalSeconds * deltaProgress, self.duration.timescale); 514 | 515 | CGFloat persent = (CMTimeGetSeconds(self.player.currentTime) + totalSeconds * deltaProgress) / totalSeconds; 516 | 517 | [self seekToCMTime:time progress:persent]; 518 | } 519 | } 520 | 521 | } 522 | 523 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 524 | { 525 | [super touchesCancelled:touches withEvent:event]; 526 | 527 | if (self.headerView.alpha == 0.0) { // 隐藏状态下 528 | 529 | } else { // 显示状态下 530 | 531 | // 手势取消后延迟调用hideHeaderViewAndBottomView 532 | [self delayHideHeaderViewAndBottomView]; 533 | } 534 | } 535 | 536 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 537 | { 538 | [super touchesEnded:touches withEvent:event]; 539 | 540 | if (self.headerView.alpha == 0.0) { // 隐藏状态下 541 | 542 | } else { // 显示状态下 543 | 544 | // 手势取消后延迟调用hideHeaderViewAndBottomView 545 | [self delayHideHeaderViewAndBottomView]; 546 | } 547 | } 548 | 549 | #pragma mark 延迟调用hideHeaderViewAndBottomView方法 550 | - (void)delayHideHeaderViewAndBottomView { 551 | [self performSelector:@selector(hideHeaderViewAndBottomView) withObject:nil afterDelay:5.0f]; 552 | } 553 | 554 | #pragma mark 取消调用某个方法 555 | - (void)cancelPerformSelector:(SEL)selector { 556 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:selector object:nil]; 557 | } 558 | 559 | #pragma mark 隐藏headerView和bottomView 560 | - (void)hideHeaderViewAndBottomView { 561 | [UIView animateWithDuration:0.5 animations:^{ 562 | [self.headerView setAlpha:0.0]; 563 | [self.bottomView setAlpha:0.0]; 564 | }]; 565 | } 566 | 567 | #pragma mark 显示headerView和bottomView 568 | - (void)showHeaderViewAndBottomView { 569 | [UIView animateWithDuration:0.5 animations:^{ 570 | [self.headerView setAlpha:ALPHA]; 571 | [self.bottomView setAlpha:ALPHA]; 572 | }]; 573 | } 574 | 575 | 576 | #pragma mark - 577 | - (void)viewWillAppear:(BOOL)animated 578 | { 579 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 580 | } 581 | 582 | #pragma mark - 状态栏 583 | - (BOOL)prefersStatusBarHidden 584 | { 585 | return YES; 586 | } 587 | 588 | #pragma mark - 屏幕方向 589 | // 允许自动旋转,在支持的屏幕中设置了允许旋转的屏幕方向。 590 | - (BOOL)shouldAutorotate 591 | { 592 | return YES; 593 | } 594 | 595 | // 支持的屏幕方向,这个方法返回 UIInterfaceOrientationMask 类型的值。 596 | - (NSUInteger)supportedInterfaceOrientations 597 | { 598 | return UIInterfaceOrientationMaskLandscape; 599 | } 600 | 601 | // 视图展示的时候优先展示为 home键在右边的 横屏 602 | - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation 603 | { 604 | return UIInterfaceOrientationLandscapeRight; 605 | } 606 | 607 | 608 | #pragma mark - 609 | 610 | - (void)dealloc 611 | { 612 | [self.player pause]; 613 | 614 | [self removeObserver:self forKeyPath:@"playerItem.status" context:playerItemStatusContext]; 615 | 616 | [self removeObserver:self forKeyPath:@"playing" context:playerPlayingContext]; 617 | 618 | [self.player removeTimeObserver:self.timeObserver]; 619 | self.timeObserver = nil; 620 | 621 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 622 | 623 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; 624 | 625 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; 626 | 627 | self.player = nil; 628 | self.playerItem = nil; 629 | self.mediaURL = nil; 630 | self.mediaTitle = nil; 631 | } 632 | 633 | - (void)didReceiveMemoryWarning { 634 | [super didReceiveMemoryWarning]; 635 | // Dispose of any resources that can be recreated. 636 | } 637 | 638 | @end 639 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/JRPlayerViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 75 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 109 | 118 | 127 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/backward_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/backward_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/backward_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/backward_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/fast_backward_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/fast_backward_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/fast_backward_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/fast_backward_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/fast_forward_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/fast_forward_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/fast_forward_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/fast_forward_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/forward_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/forward_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/forward_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/forward_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/pause_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/pause_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/pause_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/pause_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/play_disable@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/play_disable@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/play_nor@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/play_nor@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/progressThumb@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/progressThumb@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/video_brightness_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/video_brightness_bg@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/video_num_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/video_num_bg@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/video_num_front@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/video_num_front@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/video_tips@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/video_tips@2x.png -------------------------------------------------------------------------------- /JRVideoPlayerViewController/images/video_volume_bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/JRVideoPlayerViewController/images/video_volume_bg@2x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JRVideoPlayer 2 | 3 | ![JRVideoPlayer](https://github.com/zhanjiarong/JRVideoPlayer/blob/master/iOS%20Simulator%20Screen%20Shot%202015_8_4_4.27.48.png?raw=true "JRVideoPlayer") 4 | 5 | ![JRVideoPlayer](https://github.com/zhanjiarong/JRVideoPlayer/blob/master/iOS%20Simulator%20Screen%20Shot%202015_8_4_4.28.14.png?raw=true "JRVideoPlayer") 6 | 7 | ## Overview 8 | 9 | **JRVideoPlayer**, 是一个基于AVFoundation框架的视频播放器,支持常见视频格式和HTTP Live Streaming视频流。播放器是根据苹果官方文档《AVFoundation Programming Guide》写的。 10 | 11 | 代码控制了默认只允许横屏播放。 12 | 13 | 上下滑动屏幕左1/3是调亮度,下下滑动屏幕右1/3是调音量,但是只有在真机下才有效果。左右滑动中间1/3是快进快退。 14 | 15 | 16 | ## 使用方法 17 | 18 | ``` 19 | 1.将JRVideoPlayerViewController文件夹放到工程中,导入"JRPlayerViewController.h" 20 | 21 | 2.播放本地视频 22 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithLocalMediaURL:url]; 23 | 24 | 3.播放HTTPLiveStreaming视频 25 | JRPlayerViewController *playerVC = [[JRPlayerViewController alloc] initWithHTTPLiveStreamingMediaURL:url]; 26 | 27 | ``` 28 | 29 | 详情看Demo,可以结合《AVFoundation Programming Guide》来看。 -------------------------------------------------------------------------------- /iOS Simulator Screen Shot 2015_8_4_4.27.48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/iOS Simulator Screen Shot 2015_8_4_4.27.48.png -------------------------------------------------------------------------------- /iOS Simulator Screen Shot 2015_8_4_4.28.14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanjiarong/JRVideoPlayer/60eaf626319fb4e9056481bfa798e0b3780e2282/iOS Simulator Screen Shot 2015_8_4_4.28.14.png --------------------------------------------------------------------------------