├── .gitignore ├── LYPlayerDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── liyang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── liyang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── LYPlayerDemo.xcscheme │ └── xcschememanagement.plist ├── LYPlayerDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── LYVideoPlayVC.h ├── LYVideoPlayVC.m ├── LYVideoPlayer │ ├── LYDownloadManager.h │ ├── LYDownloadManager.m │ ├── LYPlayerHeader.h │ ├── LYVideoPlayer.h │ ├── LYVideoPlayer.m │ ├── LYVideoPresenter.h │ ├── LYVideoPresenter.m │ ├── category │ │ ├── NSString+LYFileHandlePath.h │ │ ├── NSString+LYFileHandlePath.m │ │ ├── UIImage+ComPress.h │ │ └── UIImage+ComPress.m │ ├── playControlView │ │ ├── LYSlider.h │ │ ├── LYSlider.m │ │ ├── LYVideoPlayerControl.h │ │ └── LYVideoPlayerControl.m │ └── resource │ │ ├── back@2x.png │ │ ├── full_screen@2x.png │ │ ├── normal_screen@2x.png │ │ ├── video_pause@2x.png │ │ └── video_play@2x.png ├── PrefixHeader.pch ├── ViewController.h ├── ViewController.m └── main.m ├── LYPlayerDemoTests ├── Info.plist └── LYPlayerDemoTests.m ├── LYPlayerDemoUITests ├── Info.plist └── LYPlayerDemoUITests.m ├── README.md ├── 个性化滑块1.png ├── 个性化滑块2.png └── 播放效果.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | build/* 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | *.xcworkspace 14 | !default.xcworkspace 15 | xcuserdata 16 | profile 17 | *.moved-aside 18 | *.xcuserstate 19 | *.xcbkptlist -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B601F7D81DD5C798000D18D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F7D71DD5C798000D18D8 /* main.m */; }; 11 | B601F7DB1DD5C798000D18D8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F7DA1DD5C798000D18D8 /* AppDelegate.m */; }; 12 | B601F7DE1DD5C798000D18D8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F7DD1DD5C798000D18D8 /* ViewController.m */; }; 13 | B601F7E11DD5C798000D18D8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B601F7DF1DD5C798000D18D8 /* Main.storyboard */; }; 14 | B601F7E31DD5C798000D18D8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B601F7E21DD5C798000D18D8 /* Assets.xcassets */; }; 15 | B601F7E61DD5C798000D18D8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B601F7E41DD5C798000D18D8 /* LaunchScreen.storyboard */; }; 16 | B601F7F11DD5C798000D18D8 /* LYPlayerDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F7F01DD5C798000D18D8 /* LYPlayerDemoTests.m */; }; 17 | B601F7FC1DD5C798000D18D8 /* LYPlayerDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F7FB1DD5C798000D18D8 /* LYPlayerDemoUITests.m */; }; 18 | B601F8311DD5CA9B000D18D8 /* LYVideoPlayVC.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8301DD5CA9B000D18D8 /* LYVideoPlayVC.m */; }; 19 | B601F8BE1DDAFFC2000D18D8 /* NSString+LYFileHandlePath.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8A81DDAFFC2000D18D8 /* NSString+LYFileHandlePath.m */; }; 20 | B601F8BF1DDAFFC2000D18D8 /* UIImage+ComPress.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8AA1DDAFFC2000D18D8 /* UIImage+ComPress.m */; }; 21 | B601F8C01DDAFFC2000D18D8 /* LYDownloadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8AC1DDAFFC2000D18D8 /* LYDownloadManager.m */; }; 22 | B601F8C11DDAFFC2000D18D8 /* LYVideoPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8AF1DDAFFC2000D18D8 /* LYVideoPlayer.m */; }; 23 | B601F8C31DDAFFC2000D18D8 /* LYSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8B41DDAFFC2000D18D8 /* LYSlider.m */; }; 24 | B601F8C41DDAFFC2000D18D8 /* LYVideoPlayerControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B601F8B61DDAFFC2000D18D8 /* LYVideoPlayerControl.m */; }; 25 | B601F8C61DDAFFC2000D18D8 /* back@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B601F8BA1DDAFFC2000D18D8 /* back@2x.png */; }; 26 | B601F8C71DDAFFC2000D18D8 /* normal_screen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B601F8BB1DDAFFC2000D18D8 /* normal_screen@2x.png */; }; 27 | B601F8C81DDAFFC2000D18D8 /* video_pause@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B601F8BC1DDAFFC2000D18D8 /* video_pause@2x.png */; }; 28 | B601F8C91DDAFFC2000D18D8 /* video_play@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B601F8BD1DDAFFC2000D18D8 /* video_play@2x.png */; }; 29 | B601F90D1DDD5861000D18D8 /* full_screen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B601F90C1DDD5861000D18D8 /* full_screen@2x.png */; }; 30 | B649A95C20A88553005F733C /* LYVideoPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = B649A95A20A88553005F733C /* LYVideoPresenter.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | B601F7ED1DD5C798000D18D8 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = B601F7CB1DD5C798000D18D8 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = B601F7D21DD5C798000D18D8; 39 | remoteInfo = LYPlayerDemo; 40 | }; 41 | B601F7F81DD5C798000D18D8 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = B601F7CB1DD5C798000D18D8 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = B601F7D21DD5C798000D18D8; 46 | remoteInfo = LYPlayerDemo; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | B601F7D31DD5C798000D18D8 /* LYPlayerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LYPlayerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | B601F7D71DD5C798000D18D8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 53 | B601F7D91DD5C798000D18D8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 54 | B601F7DA1DD5C798000D18D8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 55 | B601F7DC1DD5C798000D18D8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 56 | B601F7DD1DD5C798000D18D8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 57 | B601F7E01DD5C798000D18D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 58 | B601F7E21DD5C798000D18D8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | B601F7E51DD5C798000D18D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 60 | B601F7E71DD5C798000D18D8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | B601F7EC1DD5C798000D18D8 /* LYPlayerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LYPlayerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | B601F7F01DD5C798000D18D8 /* LYPlayerDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LYPlayerDemoTests.m; sourceTree = ""; }; 63 | B601F7F21DD5C798000D18D8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | B601F7F71DD5C798000D18D8 /* LYPlayerDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LYPlayerDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | B601F7FB1DD5C798000D18D8 /* LYPlayerDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LYPlayerDemoUITests.m; sourceTree = ""; }; 66 | B601F7FD1DD5C798000D18D8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | B601F82E1DD5C83D000D18D8 /* PrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrefixHeader.pch; sourceTree = ""; }; 68 | B601F82F1DD5CA9B000D18D8 /* LYVideoPlayVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYVideoPlayVC.h; sourceTree = ""; }; 69 | B601F8301DD5CA9B000D18D8 /* LYVideoPlayVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYVideoPlayVC.m; sourceTree = ""; }; 70 | B601F8A71DDAFFC2000D18D8 /* NSString+LYFileHandlePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+LYFileHandlePath.h"; sourceTree = ""; }; 71 | B601F8A81DDAFFC2000D18D8 /* NSString+LYFileHandlePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+LYFileHandlePath.m"; sourceTree = ""; }; 72 | B601F8A91DDAFFC2000D18D8 /* UIImage+ComPress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ComPress.h"; sourceTree = ""; }; 73 | B601F8AA1DDAFFC2000D18D8 /* UIImage+ComPress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ComPress.m"; sourceTree = ""; }; 74 | B601F8AB1DDAFFC2000D18D8 /* LYDownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYDownloadManager.h; sourceTree = ""; }; 75 | B601F8AC1DDAFFC2000D18D8 /* LYDownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYDownloadManager.m; sourceTree = ""; }; 76 | B601F8AD1DDAFFC2000D18D8 /* LYPlayerHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYPlayerHeader.h; sourceTree = ""; }; 77 | B601F8AE1DDAFFC2000D18D8 /* LYVideoPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYVideoPlayer.h; sourceTree = ""; }; 78 | B601F8AF1DDAFFC2000D18D8 /* LYVideoPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYVideoPlayer.m; sourceTree = ""; }; 79 | B601F8B31DDAFFC2000D18D8 /* LYSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYSlider.h; sourceTree = ""; }; 80 | B601F8B41DDAFFC2000D18D8 /* LYSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYSlider.m; sourceTree = ""; }; 81 | B601F8B51DDAFFC2000D18D8 /* LYVideoPlayerControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYVideoPlayerControl.h; sourceTree = ""; }; 82 | B601F8B61DDAFFC2000D18D8 /* LYVideoPlayerControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYVideoPlayerControl.m; sourceTree = ""; }; 83 | B601F8BA1DDAFFC2000D18D8 /* back@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back@2x.png"; sourceTree = ""; }; 84 | B601F8BB1DDAFFC2000D18D8 /* normal_screen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "normal_screen@2x.png"; sourceTree = ""; }; 85 | B601F8BC1DDAFFC2000D18D8 /* video_pause@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_pause@2x.png"; sourceTree = ""; }; 86 | B601F8BD1DDAFFC2000D18D8 /* video_play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "video_play@2x.png"; sourceTree = ""; }; 87 | B601F90C1DDD5861000D18D8 /* full_screen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "full_screen@2x.png"; sourceTree = ""; }; 88 | B649A95A20A88553005F733C /* LYVideoPresenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LYVideoPresenter.m; sourceTree = ""; }; 89 | B649A95B20A88553005F733C /* LYVideoPresenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LYVideoPresenter.h; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | B601F7D01DD5C798000D18D8 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | B601F7E91DD5C798000D18D8 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | B601F7F41DD5C798000D18D8 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXFrameworksBuildPhase section */ 115 | 116 | /* Begin PBXGroup section */ 117 | B601F7CA1DD5C798000D18D8 = { 118 | isa = PBXGroup; 119 | children = ( 120 | B601F7D51DD5C798000D18D8 /* LYPlayerDemo */, 121 | B601F7EF1DD5C798000D18D8 /* LYPlayerDemoTests */, 122 | B601F7FA1DD5C798000D18D8 /* LYPlayerDemoUITests */, 123 | B601F7D41DD5C798000D18D8 /* Products */, 124 | ); 125 | sourceTree = ""; 126 | }; 127 | B601F7D41DD5C798000D18D8 /* Products */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | B601F7D31DD5C798000D18D8 /* LYPlayerDemo.app */, 131 | B601F7EC1DD5C798000D18D8 /* LYPlayerDemoTests.xctest */, 132 | B601F7F71DD5C798000D18D8 /* LYPlayerDemoUITests.xctest */, 133 | ); 134 | name = Products; 135 | sourceTree = ""; 136 | }; 137 | B601F7D51DD5C798000D18D8 /* LYPlayerDemo */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | B601F7D91DD5C798000D18D8 /* AppDelegate.h */, 141 | B601F7DA1DD5C798000D18D8 /* AppDelegate.m */, 142 | B601F7DC1DD5C798000D18D8 /* ViewController.h */, 143 | B601F7DD1DD5C798000D18D8 /* ViewController.m */, 144 | B601F82F1DD5CA9B000D18D8 /* LYVideoPlayVC.h */, 145 | B601F8301DD5CA9B000D18D8 /* LYVideoPlayVC.m */, 146 | B601F8A51DDAFFC2000D18D8 /* LYVideoPlayer */, 147 | B601F7DF1DD5C798000D18D8 /* Main.storyboard */, 148 | B601F7E21DD5C798000D18D8 /* Assets.xcassets */, 149 | B601F7E41DD5C798000D18D8 /* LaunchScreen.storyboard */, 150 | B601F7E71DD5C798000D18D8 /* Info.plist */, 151 | B601F7D61DD5C798000D18D8 /* Supporting Files */, 152 | B601F82E1DD5C83D000D18D8 /* PrefixHeader.pch */, 153 | ); 154 | path = LYPlayerDemo; 155 | sourceTree = ""; 156 | }; 157 | B601F7D61DD5C798000D18D8 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | B601F7D71DD5C798000D18D8 /* main.m */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | B601F7EF1DD5C798000D18D8 /* LYPlayerDemoTests */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | B601F7F01DD5C798000D18D8 /* LYPlayerDemoTests.m */, 169 | B601F7F21DD5C798000D18D8 /* Info.plist */, 170 | ); 171 | path = LYPlayerDemoTests; 172 | sourceTree = ""; 173 | }; 174 | B601F7FA1DD5C798000D18D8 /* LYPlayerDemoUITests */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | B601F7FB1DD5C798000D18D8 /* LYPlayerDemoUITests.m */, 178 | B601F7FD1DD5C798000D18D8 /* Info.plist */, 179 | ); 180 | path = LYPlayerDemoUITests; 181 | sourceTree = ""; 182 | }; 183 | B601F8A51DDAFFC2000D18D8 /* LYVideoPlayer */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | B601F8AD1DDAFFC2000D18D8 /* LYPlayerHeader.h */, 187 | B649A95B20A88553005F733C /* LYVideoPresenter.h */, 188 | B649A95A20A88553005F733C /* LYVideoPresenter.m */, 189 | B601F8AE1DDAFFC2000D18D8 /* LYVideoPlayer.h */, 190 | B601F8AF1DDAFFC2000D18D8 /* LYVideoPlayer.m */, 191 | B601F8AB1DDAFFC2000D18D8 /* LYDownloadManager.h */, 192 | B601F8AC1DDAFFC2000D18D8 /* LYDownloadManager.m */, 193 | B601F8B01DDAFFC2000D18D8 /* playControlView */, 194 | B601F8A61DDAFFC2000D18D8 /* category */, 195 | B601F8B91DDAFFC2000D18D8 /* resource */, 196 | ); 197 | path = LYVideoPlayer; 198 | sourceTree = ""; 199 | }; 200 | B601F8A61DDAFFC2000D18D8 /* category */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | B601F8A71DDAFFC2000D18D8 /* NSString+LYFileHandlePath.h */, 204 | B601F8A81DDAFFC2000D18D8 /* NSString+LYFileHandlePath.m */, 205 | B601F8A91DDAFFC2000D18D8 /* UIImage+ComPress.h */, 206 | B601F8AA1DDAFFC2000D18D8 /* UIImage+ComPress.m */, 207 | ); 208 | path = category; 209 | sourceTree = ""; 210 | }; 211 | B601F8B01DDAFFC2000D18D8 /* playControlView */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | B601F8B51DDAFFC2000D18D8 /* LYVideoPlayerControl.h */, 215 | B601F8B61DDAFFC2000D18D8 /* LYVideoPlayerControl.m */, 216 | B601F8B31DDAFFC2000D18D8 /* LYSlider.h */, 217 | B601F8B41DDAFFC2000D18D8 /* LYSlider.m */, 218 | ); 219 | path = playControlView; 220 | sourceTree = ""; 221 | }; 222 | B601F8B91DDAFFC2000D18D8 /* resource */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | B601F8BA1DDAFFC2000D18D8 /* back@2x.png */, 226 | B601F8BB1DDAFFC2000D18D8 /* normal_screen@2x.png */, 227 | B601F90C1DDD5861000D18D8 /* full_screen@2x.png */, 228 | B601F8BC1DDAFFC2000D18D8 /* video_pause@2x.png */, 229 | B601F8BD1DDAFFC2000D18D8 /* video_play@2x.png */, 230 | ); 231 | path = resource; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXGroup section */ 235 | 236 | /* Begin PBXNativeTarget section */ 237 | B601F7D21DD5C798000D18D8 /* LYPlayerDemo */ = { 238 | isa = PBXNativeTarget; 239 | buildConfigurationList = B601F8001DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemo" */; 240 | buildPhases = ( 241 | B601F7CF1DD5C798000D18D8 /* Sources */, 242 | B601F7D01DD5C798000D18D8 /* Frameworks */, 243 | B601F7D11DD5C798000D18D8 /* Resources */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | ); 249 | name = LYPlayerDemo; 250 | productName = LYPlayerDemo; 251 | productReference = B601F7D31DD5C798000D18D8 /* LYPlayerDemo.app */; 252 | productType = "com.apple.product-type.application"; 253 | }; 254 | B601F7EB1DD5C798000D18D8 /* LYPlayerDemoTests */ = { 255 | isa = PBXNativeTarget; 256 | buildConfigurationList = B601F8031DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemoTests" */; 257 | buildPhases = ( 258 | B601F7E81DD5C798000D18D8 /* Sources */, 259 | B601F7E91DD5C798000D18D8 /* Frameworks */, 260 | B601F7EA1DD5C798000D18D8 /* Resources */, 261 | ); 262 | buildRules = ( 263 | ); 264 | dependencies = ( 265 | B601F7EE1DD5C798000D18D8 /* PBXTargetDependency */, 266 | ); 267 | name = LYPlayerDemoTests; 268 | productName = LYPlayerDemoTests; 269 | productReference = B601F7EC1DD5C798000D18D8 /* LYPlayerDemoTests.xctest */; 270 | productType = "com.apple.product-type.bundle.unit-test"; 271 | }; 272 | B601F7F61DD5C798000D18D8 /* LYPlayerDemoUITests */ = { 273 | isa = PBXNativeTarget; 274 | buildConfigurationList = B601F8061DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemoUITests" */; 275 | buildPhases = ( 276 | B601F7F31DD5C798000D18D8 /* Sources */, 277 | B601F7F41DD5C798000D18D8 /* Frameworks */, 278 | B601F7F51DD5C798000D18D8 /* Resources */, 279 | ); 280 | buildRules = ( 281 | ); 282 | dependencies = ( 283 | B601F7F91DD5C798000D18D8 /* PBXTargetDependency */, 284 | ); 285 | name = LYPlayerDemoUITests; 286 | productName = LYPlayerDemoUITests; 287 | productReference = B601F7F71DD5C798000D18D8 /* LYPlayerDemoUITests.xctest */; 288 | productType = "com.apple.product-type.bundle.ui-testing"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | B601F7CB1DD5C798000D18D8 /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastUpgradeCheck = 0800; 297 | ORGANIZATIONNAME = com.liyang.player; 298 | TargetAttributes = { 299 | B601F7D21DD5C798000D18D8 = { 300 | CreatedOnToolsVersion = 8.0; 301 | DevelopmentTeam = EQXM5L9243; 302 | ProvisioningStyle = Automatic; 303 | }; 304 | B601F7EB1DD5C798000D18D8 = { 305 | CreatedOnToolsVersion = 8.0; 306 | ProvisioningStyle = Automatic; 307 | TestTargetID = B601F7D21DD5C798000D18D8; 308 | }; 309 | B601F7F61DD5C798000D18D8 = { 310 | CreatedOnToolsVersion = 8.0; 311 | ProvisioningStyle = Automatic; 312 | TestTargetID = B601F7D21DD5C798000D18D8; 313 | }; 314 | }; 315 | }; 316 | buildConfigurationList = B601F7CE1DD5C798000D18D8 /* Build configuration list for PBXProject "LYPlayerDemo" */; 317 | compatibilityVersion = "Xcode 3.2"; 318 | developmentRegion = English; 319 | hasScannedForEncodings = 0; 320 | knownRegions = ( 321 | en, 322 | Base, 323 | ); 324 | mainGroup = B601F7CA1DD5C798000D18D8; 325 | productRefGroup = B601F7D41DD5C798000D18D8 /* Products */; 326 | projectDirPath = ""; 327 | projectRoot = ""; 328 | targets = ( 329 | B601F7D21DD5C798000D18D8 /* LYPlayerDemo */, 330 | B601F7EB1DD5C798000D18D8 /* LYPlayerDemoTests */, 331 | B601F7F61DD5C798000D18D8 /* LYPlayerDemoUITests */, 332 | ); 333 | }; 334 | /* End PBXProject section */ 335 | 336 | /* Begin PBXResourcesBuildPhase section */ 337 | B601F7D11DD5C798000D18D8 /* Resources */ = { 338 | isa = PBXResourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | B601F8C71DDAFFC2000D18D8 /* normal_screen@2x.png in Resources */, 342 | B601F7E61DD5C798000D18D8 /* LaunchScreen.storyboard in Resources */, 343 | B601F8C91DDAFFC2000D18D8 /* video_play@2x.png in Resources */, 344 | B601F8C61DDAFFC2000D18D8 /* back@2x.png in Resources */, 345 | B601F8C81DDAFFC2000D18D8 /* video_pause@2x.png in Resources */, 346 | B601F7E31DD5C798000D18D8 /* Assets.xcassets in Resources */, 347 | B601F7E11DD5C798000D18D8 /* Main.storyboard in Resources */, 348 | B601F90D1DDD5861000D18D8 /* full_screen@2x.png in Resources */, 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | B601F7EA1DD5C798000D18D8 /* Resources */ = { 353 | isa = PBXResourcesBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | ); 357 | runOnlyForDeploymentPostprocessing = 0; 358 | }; 359 | B601F7F51DD5C798000D18D8 /* Resources */ = { 360 | isa = PBXResourcesBuildPhase; 361 | buildActionMask = 2147483647; 362 | files = ( 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | /* End PBXResourcesBuildPhase section */ 367 | 368 | /* Begin PBXSourcesBuildPhase section */ 369 | B601F7CF1DD5C798000D18D8 /* Sources */ = { 370 | isa = PBXSourcesBuildPhase; 371 | buildActionMask = 2147483647; 372 | files = ( 373 | B601F8C41DDAFFC2000D18D8 /* LYVideoPlayerControl.m in Sources */, 374 | B601F8311DD5CA9B000D18D8 /* LYVideoPlayVC.m in Sources */, 375 | B601F8C01DDAFFC2000D18D8 /* LYDownloadManager.m in Sources */, 376 | B601F7DE1DD5C798000D18D8 /* ViewController.m in Sources */, 377 | B601F8C11DDAFFC2000D18D8 /* LYVideoPlayer.m in Sources */, 378 | B601F7DB1DD5C798000D18D8 /* AppDelegate.m in Sources */, 379 | B601F8BF1DDAFFC2000D18D8 /* UIImage+ComPress.m in Sources */, 380 | B649A95C20A88553005F733C /* LYVideoPresenter.m in Sources */, 381 | B601F8BE1DDAFFC2000D18D8 /* NSString+LYFileHandlePath.m in Sources */, 382 | B601F8C31DDAFFC2000D18D8 /* LYSlider.m in Sources */, 383 | B601F7D81DD5C798000D18D8 /* main.m in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | B601F7E81DD5C798000D18D8 /* Sources */ = { 388 | isa = PBXSourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | B601F7F11DD5C798000D18D8 /* LYPlayerDemoTests.m in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | B601F7F31DD5C798000D18D8 /* Sources */ = { 396 | isa = PBXSourcesBuildPhase; 397 | buildActionMask = 2147483647; 398 | files = ( 399 | B601F7FC1DD5C798000D18D8 /* LYPlayerDemoUITests.m in Sources */, 400 | ); 401 | runOnlyForDeploymentPostprocessing = 0; 402 | }; 403 | /* End PBXSourcesBuildPhase section */ 404 | 405 | /* Begin PBXTargetDependency section */ 406 | B601F7EE1DD5C798000D18D8 /* PBXTargetDependency */ = { 407 | isa = PBXTargetDependency; 408 | target = B601F7D21DD5C798000D18D8 /* LYPlayerDemo */; 409 | targetProxy = B601F7ED1DD5C798000D18D8 /* PBXContainerItemProxy */; 410 | }; 411 | B601F7F91DD5C798000D18D8 /* PBXTargetDependency */ = { 412 | isa = PBXTargetDependency; 413 | target = B601F7D21DD5C798000D18D8 /* LYPlayerDemo */; 414 | targetProxy = B601F7F81DD5C798000D18D8 /* PBXContainerItemProxy */; 415 | }; 416 | /* End PBXTargetDependency section */ 417 | 418 | /* Begin PBXVariantGroup section */ 419 | B601F7DF1DD5C798000D18D8 /* Main.storyboard */ = { 420 | isa = PBXVariantGroup; 421 | children = ( 422 | B601F7E01DD5C798000D18D8 /* Base */, 423 | ); 424 | name = Main.storyboard; 425 | sourceTree = ""; 426 | }; 427 | B601F7E41DD5C798000D18D8 /* LaunchScreen.storyboard */ = { 428 | isa = PBXVariantGroup; 429 | children = ( 430 | B601F7E51DD5C798000D18D8 /* Base */, 431 | ); 432 | name = LaunchScreen.storyboard; 433 | sourceTree = ""; 434 | }; 435 | /* End PBXVariantGroup section */ 436 | 437 | /* Begin XCBuildConfiguration section */ 438 | B601F7FE1DD5C798000D18D8 /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ALWAYS_SEARCH_USER_PATHS = NO; 442 | CLANG_ANALYZER_NONNULL = YES; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_WARN_BOOL_CONVERSION = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 450 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INFINITE_RECURSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 456 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 457 | CLANG_WARN_UNREACHABLE_CODE = YES; 458 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 459 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 460 | COPY_PHASE_STRIP = NO; 461 | DEBUG_INFORMATION_FORMAT = dwarf; 462 | ENABLE_STRICT_OBJC_MSGSEND = YES; 463 | ENABLE_TESTABILITY = YES; 464 | GCC_C_LANGUAGE_STANDARD = gnu99; 465 | GCC_DYNAMIC_NO_PIC = NO; 466 | GCC_NO_COMMON_BLOCKS = YES; 467 | GCC_OPTIMIZATION_LEVEL = 0; 468 | GCC_PREPROCESSOR_DEFINITIONS = ( 469 | "DEBUG=1", 470 | "$(inherited)", 471 | ); 472 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 473 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 474 | GCC_WARN_UNDECLARED_SELECTOR = YES; 475 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 476 | GCC_WARN_UNUSED_FUNCTION = YES; 477 | GCC_WARN_UNUSED_VARIABLE = YES; 478 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 479 | MTL_ENABLE_DEBUG_INFO = YES; 480 | ONLY_ACTIVE_ARCH = YES; 481 | SDKROOT = iphoneos; 482 | TARGETED_DEVICE_FAMILY = "1,2"; 483 | }; 484 | name = Debug; 485 | }; 486 | B601F7FF1DD5C798000D18D8 /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ALWAYS_SEARCH_USER_PATHS = NO; 490 | CLANG_ANALYZER_NONNULL = YES; 491 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 492 | CLANG_CXX_LIBRARY = "libc++"; 493 | CLANG_ENABLE_MODULES = YES; 494 | CLANG_ENABLE_OBJC_ARC = YES; 495 | CLANG_WARN_BOOL_CONVERSION = YES; 496 | CLANG_WARN_CONSTANT_CONVERSION = YES; 497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 498 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 499 | CLANG_WARN_EMPTY_BODY = YES; 500 | CLANG_WARN_ENUM_CONVERSION = YES; 501 | CLANG_WARN_INFINITE_RECURSION = YES; 502 | CLANG_WARN_INT_CONVERSION = YES; 503 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 504 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 505 | CLANG_WARN_UNREACHABLE_CODE = YES; 506 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 507 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 508 | COPY_PHASE_STRIP = NO; 509 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 510 | ENABLE_NS_ASSERTIONS = NO; 511 | ENABLE_STRICT_OBJC_MSGSEND = YES; 512 | GCC_C_LANGUAGE_STANDARD = gnu99; 513 | GCC_NO_COMMON_BLOCKS = YES; 514 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 515 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 516 | GCC_WARN_UNDECLARED_SELECTOR = YES; 517 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 518 | GCC_WARN_UNUSED_FUNCTION = YES; 519 | GCC_WARN_UNUSED_VARIABLE = YES; 520 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 521 | MTL_ENABLE_DEBUG_INFO = NO; 522 | SDKROOT = iphoneos; 523 | TARGETED_DEVICE_FAMILY = "1,2"; 524 | VALIDATE_PRODUCT = YES; 525 | }; 526 | name = Release; 527 | }; 528 | B601F8011DD5C798000D18D8 /* Debug */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | DEVELOPMENT_TEAM = EQXM5L9243; 533 | GCC_PREFIX_HEADER = "$(SRCROOT)/LYPlayerDemo/PrefixHeader.pch"; 534 | INFOPLIST_FILE = LYPlayerDemo/Info.plist; 535 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 537 | PRODUCT_BUNDLE_IDENTIFIER = com.liyang.player; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | }; 540 | name = Debug; 541 | }; 542 | B601F8021DD5C798000D18D8 /* Release */ = { 543 | isa = XCBuildConfiguration; 544 | buildSettings = { 545 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 546 | DEVELOPMENT_TEAM = EQXM5L9243; 547 | GCC_PREFIX_HEADER = "$(SRCROOT)/LYPlayerDemo/PrefixHeader.pch"; 548 | INFOPLIST_FILE = LYPlayerDemo/Info.plist; 549 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 550 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 551 | PRODUCT_BUNDLE_IDENTIFIER = com.liyang.player; 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | }; 554 | name = Release; 555 | }; 556 | B601F8041DD5C798000D18D8 /* Debug */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | BUNDLE_LOADER = "$(TEST_HOST)"; 560 | INFOPLIST_FILE = LYPlayerDemoTests/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 562 | PRODUCT_BUNDLE_IDENTIFIER = LY.LYPlayerDemoTests; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LYPlayerDemo.app/LYPlayerDemo"; 565 | }; 566 | name = Debug; 567 | }; 568 | B601F8051DD5C798000D18D8 /* Release */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | BUNDLE_LOADER = "$(TEST_HOST)"; 572 | INFOPLIST_FILE = LYPlayerDemoTests/Info.plist; 573 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 574 | PRODUCT_BUNDLE_IDENTIFIER = LY.LYPlayerDemoTests; 575 | PRODUCT_NAME = "$(TARGET_NAME)"; 576 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LYPlayerDemo.app/LYPlayerDemo"; 577 | }; 578 | name = Release; 579 | }; 580 | B601F8071DD5C798000D18D8 /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | buildSettings = { 583 | INFOPLIST_FILE = LYPlayerDemoUITests/Info.plist; 584 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 585 | PRODUCT_BUNDLE_IDENTIFIER = LY.LYPlayerDemoUITests; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | TEST_TARGET_NAME = LYPlayerDemo; 588 | }; 589 | name = Debug; 590 | }; 591 | B601F8081DD5C798000D18D8 /* Release */ = { 592 | isa = XCBuildConfiguration; 593 | buildSettings = { 594 | INFOPLIST_FILE = LYPlayerDemoUITests/Info.plist; 595 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 596 | PRODUCT_BUNDLE_IDENTIFIER = LY.LYPlayerDemoUITests; 597 | PRODUCT_NAME = "$(TARGET_NAME)"; 598 | TEST_TARGET_NAME = LYPlayerDemo; 599 | }; 600 | name = Release; 601 | }; 602 | /* End XCBuildConfiguration section */ 603 | 604 | /* Begin XCConfigurationList section */ 605 | B601F7CE1DD5C798000D18D8 /* Build configuration list for PBXProject "LYPlayerDemo" */ = { 606 | isa = XCConfigurationList; 607 | buildConfigurations = ( 608 | B601F7FE1DD5C798000D18D8 /* Debug */, 609 | B601F7FF1DD5C798000D18D8 /* Release */, 610 | ); 611 | defaultConfigurationIsVisible = 0; 612 | defaultConfigurationName = Release; 613 | }; 614 | B601F8001DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemo" */ = { 615 | isa = XCConfigurationList; 616 | buildConfigurations = ( 617 | B601F8011DD5C798000D18D8 /* Debug */, 618 | B601F8021DD5C798000D18D8 /* Release */, 619 | ); 620 | defaultConfigurationIsVisible = 0; 621 | defaultConfigurationName = Release; 622 | }; 623 | B601F8031DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemoTests" */ = { 624 | isa = XCConfigurationList; 625 | buildConfigurations = ( 626 | B601F8041DD5C798000D18D8 /* Debug */, 627 | B601F8051DD5C798000D18D8 /* Release */, 628 | ); 629 | defaultConfigurationIsVisible = 0; 630 | defaultConfigurationName = Release; 631 | }; 632 | B601F8061DD5C798000D18D8 /* Build configuration list for PBXNativeTarget "LYPlayerDemoUITests" */ = { 633 | isa = XCConfigurationList; 634 | buildConfigurations = ( 635 | B601F8071DD5C798000D18D8 /* Debug */, 636 | B601F8081DD5C798000D18D8 /* Release */, 637 | ); 638 | defaultConfigurationIsVisible = 0; 639 | defaultConfigurationName = Release; 640 | }; 641 | /* End XCConfigurationList section */ 642 | }; 643 | rootObject = B601F7CB1DD5C798000D18D8 /* Project object */; 644 | } 645 | -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/project.xcworkspace/xcuserdata/liyang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo.xcodeproj/project.xcworkspace/xcuserdata/liyang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/xcuserdata/liyang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/xcuserdata/liyang.xcuserdatad/xcschemes/LYPlayerDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /LYPlayerDemo.xcodeproj/xcuserdata/liyang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LYPlayerDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | B601F7D21DD5C798000D18D8 16 | 17 | primary 18 | 19 | 20 | B601F7EB1DD5C798000D18D8 21 | 22 | primary 23 | 24 | 25 | B601F7F61DD5C798000D18D8 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LYPlayerDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. 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 | -------------------------------------------------------------------------------- /LYPlayerDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. 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 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /LYPlayerDemo/Assets.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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /LYPlayerDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LYPlayerDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /LYPlayerDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | CFBundleDevelopmentRegion 11 | en 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleVersion 25 | 1 26 | LSRequiresIPhoneOS 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayVC.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LYVideoPlayVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayVC.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "LYVideoPlayVC.h" 10 | #import "LYVideoPresenter.h" 11 | 12 | @interface LYVideoPlayVC () 13 | 14 | @property (nonatomic ,strong) UIView *videoPlayBGView; 15 | @property (nonatomic ,copy) NSString*videoUrl; 16 | @property (nonatomic, strong) LYVideoPresenter *videpPresenter; 17 | 18 | @end 19 | 20 | @implementation LYVideoPlayVC{ 21 | BOOL _isHalfScreen; 22 | } 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | [self presenter]; 28 | } 29 | 30 | - (void)presenter{ 31 | 32 | _isHalfScreen = YES; 33 | 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | 36 | self.videoPlayBGView = [[UIView alloc] initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.width * 0.6)]; 37 | self.videoPlayBGView.backgroundColor = [UIColor blackColor]; 38 | [self.view addSubview:self.videoPlayBGView]; 39 | 40 | self.videoUrl = @"http://videoplay.elearnmooc.com/moocMain/video/e08956a5-df94-4856-96ad-e35bdbc884c4.mp4"; 41 | 42 | _videpPresenter = [[LYVideoPresenter alloc] init]; 43 | _videpPresenter.delegate = self; 44 | [_videpPresenter playWithUrl:self.videoUrl addInView:self.videoPlayBGView]; 45 | } 46 | 47 | - (void)videoPresenterDidBackBtnClick{ 48 | [self dismissViewControllerAnimated:YES completion:nil]; 49 | } 50 | 51 | @end 52 | 53 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYDownloadManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYDownloadManager.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import 12 | 13 | @class LYDownloadManager; 14 | 15 | @protocol LYDownloadManagerDelegate 16 | 17 | @optional 18 | 19 | /** 没有完整的缓存文件,告诉播放器自己去用 网络地址 进行播放 */ 20 | - (void)didNoCacheFileWithManager:(LYDownloadManager *)manager; 21 | 22 | /** 已经存在下载好的这个文件了,告诉播放器可以直接利用filePath播放 */ 23 | -(void)didFileExistedWithManager:(LYDownloadManager *)manager Path:(NSString *)filePath; 24 | 25 | /** 开始下载数据(包括长度和类型) */ 26 | - (void)didStartReceiveManager:(LYDownloadManager *)manager VideoLength:(NSUInteger)videoLength; 27 | 28 | /** 正在下载 */ 29 | - (void)didReceiveManager:(LYDownloadManager *)manager Progress:(CGFloat)progress; 30 | 31 | /** 完成下载 */ 32 | - (void)didFinishLoadingWithManager:(LYDownloadManager *)manager fileSavePath:(NSString *)filePath; 33 | 34 | /** 下载失败(错误码) */ 35 | - (void)didFailLoadingWithManager:(LYDownloadManager *)manager WithError:(NSError *)errorCode; 36 | 37 | @end 38 | 39 | @interface LYDownloadManager : NSObject 40 | 41 | @property(nonatomic, weak) id delegate; 42 | 43 | //定义初始化方法 传入videoUrl参数(NSString) 44 | - (instancetype)initWithURL:(NSString *)videoUrl withDelegate:(id)delegate; 45 | 46 | /** 开始下载 */ 47 | - (void)start; 48 | /** 暂停 */ 49 | - (void)suspend; 50 | /** 关闭 */ 51 | - (void)cancel; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYDownloadManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYDownloadManager.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "LYDownloadManager.h" 10 | 11 | @interface LYDownloadManager () 12 | 13 | @property (nonatomic ,strong) NSURLSession *session; 14 | @property (nonatomic ,strong) NSURLSessionDataTask *dataTask; 15 | 16 | @property (nonatomic ,strong) NSFileHandle *fileHandle; 17 | @property (nonatomic ,strong) NSFileManager *fileManager; 18 | 19 | @property (nonatomic ,assign) NSInteger curruentLength; 20 | @property (nonatomic ,assign) NSInteger totalLength; 21 | 22 | @property (nonatomic ,copy) NSString *videoUrl; 23 | @property (nonatomic ,copy) NSString *videoTempPath; 24 | @property (nonatomic ,copy) NSString *videoCachePath; 25 | 26 | @end 27 | 28 | @implementation LYDownloadManager 29 | 30 | - (instancetype)initWithURL:(NSString *)videoUrl withDelegate:(id)delegate 31 | { 32 | self = [super init]; 33 | if (self) { 34 | 35 | self.videoUrl = videoUrl; 36 | self.delegate = delegate; 37 | 38 | [self fileJudge]; 39 | } 40 | return self; 41 | } 42 | - (void)fileJudge{ 43 | 44 | //自定的操作 45 | _fileManager = [NSFileManager defaultManager]; 46 | 47 | NSString *videoName = [[self.videoUrl componentsSeparatedByString:@"/"] lastObject]; 48 | 49 | //定义文件的临时下载路径 50 | self.videoTempPath = [NSString tempFilePathWithFileName:videoName]; 51 | 52 | //定义文件的缓存路径 53 | self.videoCachePath = [NSString cacheFilePathWithName:videoName]; 54 | 55 | NSLog(@"videoTempPath === %@", self.videoTempPath); 56 | 57 | if ([_fileManager fileExistsAtPath:self.videoCachePath]) {//缓存目录下已存在下载完成的文件 58 | 59 | if (self.delegate && [self.delegate respondsToSelector:@selector(didFileExistedWithManager:Path:)]) { 60 | [self.delegate didFileExistedWithManager:self Path:self.videoCachePath]; 61 | } 62 | 63 | }else{ 64 | //判断当前目录下有无已有下载的临时文件 65 | if ([_fileManager fileExistsAtPath:self.videoTempPath]) { 66 | //可以读到 67 | _fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:self.videoTempPath]; 68 | //存在已下载数据的文件 69 | _curruentLength = [_fileHandle seekToEndOfFile]; 70 | 71 | }else{ 72 | //不存在文件 73 | _curruentLength = 0; 74 | //创建文件 75 | [_fileManager createFileAtPath:self.videoTempPath contents:nil attributes:nil]; 76 | //创建之后再读 77 | _fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:self.videoTempPath]; 78 | } 79 | 80 | [self sendHttpRequst]; 81 | 82 | if (self.delegate && [self.delegate respondsToSelector:@selector(didNoCacheFileWithManager:)]) { 83 | [self.delegate didNoCacheFileWithManager:self]; 84 | } 85 | } 86 | } 87 | - (NSURLSession *)session{ 88 | if (!_session) { 89 | _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 90 | } 91 | return _session; 92 | } 93 | 94 | //网路请求方法 95 | - (void)sendHttpRequst 96 | { 97 | [_fileHandle seekToEndOfFile]; 98 | NSURL *url = [NSURL URLWithString:_videoUrl]; 99 | NSMutableURLRequest *requeset = [NSMutableURLRequest requestWithURL:url]; 100 | 101 | //指定头信息 当前已下载的进度 102 | [requeset setValue:[NSString stringWithFormat:@"bytes=%ld-", _curruentLength] forHTTPHeaderField:@"Range"]; 103 | 104 | //创建请求 105 | NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:requeset]; 106 | self.dataTask = dataTask; 107 | 108 | //发起请求 109 | [self.dataTask resume]; 110 | } 111 | 112 | -(void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask 113 | didReceiveResponse:(nonnull NSURLResponse *)response 114 | completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler { 115 | 116 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; 117 | NSDictionary *dic = (NSDictionary *)[httpResponse allHeaderFields] ; 118 | NSString *content = [dic valueForKey:@"Content-Range"]; 119 | NSArray *array = [content componentsSeparatedByString:@"/"]; 120 | NSString *length = array.lastObject; 121 | 122 | NSUInteger videoLength; 123 | if ([length integerValue] == 0) { 124 | videoLength = (NSUInteger)httpResponse.expectedContentLength; 125 | } else { 126 | videoLength = [length integerValue]; 127 | } 128 | 129 | //接受到响应的时候 告诉系统如何处理服务器返回的数据 130 | completionHandler(NSURLSessionResponseAllow); 131 | //得到请求文件的数据大小 132 | _totalLength = response.expectedContentLength + _curruentLength; 133 | 134 | if (self.delegate && [self.delegate respondsToSelector:@selector(didStartReceiveManager:VideoLength:)]) { 135 | [self.delegate didStartReceiveManager:self VideoLength:videoLength]; 136 | } 137 | } 138 | -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 139 | 140 | //使用文件句柄指针来写数据(边写边移动) 141 | [_fileHandle writeData:data]; 142 | 143 | //累加已经下载的文件数据大小 144 | _curruentLength += data.length; 145 | 146 | //计算文件的下载进度 = 已经下载的 / 文件的总大小 147 | CGFloat progress = 1.0 * _curruentLength / _totalLength; 148 | 149 | if (self.delegate && [self.delegate respondsToSelector:@selector(didReceiveManager:Progress:)]) { 150 | [self.delegate didReceiveManager:self Progress:progress]; 151 | } 152 | 153 | NSLog(@"progress = %f",progress); 154 | 155 | } 156 | -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 157 | 158 | if (error == nil) { //下载成功 159 | //当前下载的文件路径 160 | NSURL *tempPathURL = [NSURL fileURLWithPath:self.videoTempPath]; 161 | 162 | NSURL *cachefileURL = [NSURL fileURLWithPath:self.videoCachePath]; 163 | 164 | NSLog(@"videoCachePath === %@", self.videoCachePath); 165 | 166 | // 如果没有该文件夹,创建文件夹 167 | if (![self.fileManager fileExistsAtPath:self.videoCachePath]) { 168 | [self.fileManager createDirectoryAtPath:self.videoCachePath withIntermediateDirectories:YES attributes:nil error:nil]; 169 | } 170 | 171 | // 如果该路径下文件已经存在,就要先将其移除,在移动文件 172 | if ([self.fileManager fileExistsAtPath:[cachefileURL path] isDirectory:NULL]) { 173 | [self.fileManager removeItemAtURL:cachefileURL error:NULL]; 174 | } 175 | //移动文件至缓存目录 176 | [self.fileManager moveItemAtURL:tempPathURL toURL:cachefileURL error:NULL]; 177 | 178 | if (self.delegate && [self.delegate respondsToSelector:@selector(didFinishLoadingWithManager:fileSavePath:)]) { 179 | [self.delegate didFinishLoadingWithManager:self fileSavePath:self.videoCachePath]; 180 | } 181 | }else{//下载失败 182 | if (self.delegate && [self.delegate respondsToSelector:@selector(didFailLoadingWithManager:WithError:)]) { 183 | [self.delegate didFailLoadingWithManager:self WithError:error]; 184 | } 185 | } 186 | } 187 | 188 | //开始/继续 189 | - (void)start{ 190 | if (self.dataTask == nil) { 191 | [self sendHttpRequst]; 192 | }else{ 193 | 194 | [self.dataTask resume]; 195 | } 196 | } 197 | - (void)suspend{ 198 | [self.dataTask suspend]; 199 | } 200 | - (void)cancel{ 201 | [self.dataTask cancel]; 202 | self.dataTask = nil; 203 | } 204 | 205 | @end 206 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYPlayerHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYPlayerHeader.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #ifndef LYPlayerHeader_h 11 | #define LYPlayerHeader_h 12 | 13 | #import "NSString+LYFileHandlePath.h" 14 | #import "UIImage+ComPress.h" 15 | 16 | //屏幕宽 17 | #define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width 18 | #define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height 19 | 20 | 21 | #endif /* LYPlayerHeader_h */ 22 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYVideoPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayer.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class LYVideoPlayer; 13 | 14 | @protocol LYVideoPlayerDelegate 15 | 16 | @optional 17 | /** 18 | 开始缓冲 19 | */ 20 | - (void)videoPlayerDidStartBuffer; 21 | /** 22 | 缓冲完成 23 | */ 24 | - (void)videoPlayerDidEndBuffer; 25 | 26 | /** 27 | 播放器已经开始播放了 28 | */ 29 | - (void)videoPlayerDidPlay; 30 | /** 31 | 播放器已经暂停了播放 32 | */ 33 | - (void)videoPlayerDidPause; 34 | /** 35 | 播放器播放结束 36 | */ 37 | - (void)videoPlayerDidPlayToEnd; 38 | 39 | // ------------------------- 回调一些视频信息 --------------------------- 40 | /** 41 | @param totalTime 视频总长度(秒) 42 | */ 43 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer totalTime:(NSInteger)totalTime; 44 | 45 | /** 46 | @param currentTime 当前播放进度(秒) 47 | */ 48 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer currentTime:(NSInteger)currentTime; 49 | 50 | /** 51 | @param bufferProgress 当前缓冲进度比例(0~1) 52 | */ 53 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer bufferProgress:(CGFloat)bufferProgress; 54 | 55 | @end 56 | 57 | @interface LYVideoPlayer : NSObject 58 | 59 | @property (nonatomic, weak) id delegate; 60 | 61 | /// 静音 default is NO 62 | @property (nonatomic, assign) BOOL mute; 63 | 64 | /// 当APP进入后台时是否暂停播放,default is YES 65 | @property (nonatomic, assign) BOOL stopWhenAppDidEnterBackground; 66 | 67 | /// 当视频播放结束时是否重新播放 default is NO 68 | @property (nonatomic, assign) BOOL replayWhenVideoPlayEnd; 69 | 70 | /// 可给定video尺寸大小,若尺寸超过view大小时作截断处理 71 | @property (nonatomic, assign) CGSize videoSize; 72 | 73 | /** 74 | 根据url播放视频(初始化播放) 75 | */ 76 | - (void)playWithUrl:(NSString *)url showView:(UIView *)showView; 77 | 78 | /** 79 | 播放 80 | */ 81 | - (void)playVideo; 82 | 83 | /** 84 | 暂停 85 | */ 86 | - (void)pauseVideo; 87 | 88 | /** 89 | 停止播放/清空播放器 90 | */ 91 | - (void)stopVideo; 92 | 93 | /** 94 | @param toTime 从指定的时间开始播放(秒) 95 | */ 96 | - (void)seekToTimePlay:(float)toTime; 97 | 98 | /** 99 | 横竖屏转换 100 | */ 101 | - (void)fullScreenChanged:(CGRect)frame; 102 | 103 | @end 104 | 105 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYVideoPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayer.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #import "LYVideoPlayer.h" 11 | 12 | @interface LYVideoPlayer () 13 | 14 | @property (nonatomic, strong) AVPlayer *player; 15 | 16 | @property (nonatomic, strong) AVPlayerItem *currentPlayerItem; 17 | 18 | @property (nonatomic, strong) AVPlayerLayer *currentPlayerLayer; 19 | 20 | @property (nonatomic, strong) UIView *videoShowView; //用于视频显示的View 21 | 22 | @property (nonatomic, strong) NSString *videoUrl; //视频地址 23 | 24 | @property (nonatomic, strong) id timeObserve; //监听播放进度 25 | 26 | @property (nonatomic, assign) CGFloat duration; //视频时间总长度(秒) 27 | @property (nonatomic, assign) CGFloat currentTime; //视频当前播放进度(秒) 28 | 29 | @property (nonatomic, assign) BOOL playButtonState;//playButtonState 用于 缓冲达到要求值的情况时如果状态是暂停,则不会自动播放 30 | 31 | @property (nonatomic, assign) BOOL isCanToGetLocalTime; //是否能去获取本地时间(秒) 32 | @property (nonatomic, assign) NSInteger localTime; //当前本地时间 33 | @property (nonatomic, strong) NSMutableArray *loadedTimeRangeArr;//存储缓冲范围的数组(当拖动滑块时,AVPlayerItem会生成另一个缓冲区域) 34 | 35 | @property (nonatomic, assign) BOOL isPlaying; //是否正在播放 36 | @property (nonatomic, assign) BOOL isBufferEmpty; //没有缓冲数据 37 | @property (nonatomic, assign) CGFloat lastBufferValue; //记录上次的缓冲值 38 | @property (nonatomic, assign) CGFloat currentBufferValue;//当前的缓冲值 39 | 40 | @end 41 | 42 | @implementation LYVideoPlayer 43 | 44 | - (void)playWithUrl:(NSString *)videoUrl showView:(UIView *)showView{ 45 | self.videoUrl = videoUrl; 46 | 47 | _videoShowView = showView; 48 | _videoShowView.layer.masksToBounds = YES; 49 | 50 | NSURL *url = [NSURL URLWithString:self.videoUrl]; 51 | [self getUrlToPlayVideo:url]; 52 | } 53 | 54 | //播放前需要初始化的一些配置 55 | - (void)configureAndNotification{ 56 | 57 | self.stopWhenAppDidEnterBackground = YES; 58 | self.playButtonState = YES; 59 | self.isPlaying = NO; 60 | self.isCanToGetLocalTime = YES; 61 | self.loadedTimeRangeArr = [NSMutableArray array]; 62 | 63 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; 64 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; 65 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 66 | } 67 | 68 | #pragma mark - Setter/Getter 69 | //静音 70 | - (void)setMute:(BOOL)mute{ 71 | _mute = mute; 72 | self.player.muted = _mute; 73 | } 74 | - (void)setVideoSize:(CGSize)videoSize{ 75 | _videoSize = videoSize; 76 | 77 | if (self.currentPlayerLayer) { 78 | [self changePlayerLayerFrameWithVideoSize:_videoSize]; 79 | } 80 | } 81 | 82 | #pragma mark - Private methods 83 | //给定URL, 创建播放器 播放 84 | - (void)getUrlToPlayVideo:(NSURL *)url{ 85 | //清空配置 86 | [self stopVideo]; 87 | 88 | //初始化一些配置 89 | [self configureAndNotification]; 90 | 91 | //创建播放器 92 | self.currentPlayerItem = [AVPlayerItem playerItemWithURL:url]; 93 | self.player = [AVPlayer playerWithPlayerItem:self.currentPlayerItem]; 94 | self.currentPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 95 | 96 | //设置layer的frame 97 | [self changePlayerLayerFrameWithVideoSize:self.videoSize]; 98 | 99 | //添加KVO 100 | [self addObserver]; 101 | } 102 | 103 | - (void)changePlayerLayerFrameWithVideoSize:(CGSize)videoSize{ 104 | 105 | if (videoSize.width) { 106 | 107 | CGSize size; 108 | size.width = self.videoShowView.bounds.size.width; 109 | size.height = size.width / videoSize.width * videoSize.height; 110 | 111 | CGFloat x = 0; 112 | CGFloat y = (self.videoShowView.bounds.size.height - size.height) * 0.5; 113 | 114 | self.currentPlayerLayer.frame = CGRectMake(x, y, size.width, size.height); 115 | 116 | }else{ 117 | self.currentPlayerLayer.frame = CGRectMake(0, 0, _videoShowView.bounds.size.width, _videoShowView.bounds.size.height); 118 | } 119 | 120 | } 121 | 122 | //视频UI显示 123 | -(void)handleShowViewSublayers{ 124 | for (CALayer *layer in _videoShowView.subviews) { 125 | [layer removeFromSuperlayer]; 126 | } 127 | [_videoShowView.layer addSublayer:self.currentPlayerLayer]; 128 | } 129 | 130 | //拖动 131 | - (void)seekToTimePlay:(float)toTime{ 132 | 133 | if (self.player) { 134 | [self.player pause]; 135 | 136 | //手动添加缓冲区域数组,最后根据toTime在数组中遍历以计算拖动的时间是否在缓冲区域内 137 | [self.loadedTimeRangeArr addObject:[self getLoadedTimeRange]]; 138 | BOOL isShowActivity = [self judgeLoadedTimeIsShowActivity:toTime]; 139 | if (isShowActivity) { 140 | [self delegateDidStartBuffer]; 141 | } 142 | self.isCanToGetLocalTime = NO; //拖动时停止获取本地时间 143 | __weak typeof(self) weak_self = self; 144 | [self.player seekToTime:CMTimeMake(toTime, 1) completionHandler:^(BOOL finished) { 145 | __strong typeof(weak_self) strong_self = weak_self; 146 | if (!strong_self) return; 147 | [strong_self play]; 148 | }]; 149 | } 150 | } 151 | 152 | //横竖屏转换 153 | - (void)fullScreenChanged:(CGRect)frame{ 154 | self.videoShowView.frame = frame; 155 | self.currentPlayerLayer.frame = frame; 156 | } 157 | 158 | // 计算缓冲总进度 159 | - (NSTimeInterval)availableDuration { 160 | NSArray *loadedTimeRanges = [self.currentPlayerItem loadedTimeRanges]; 161 | CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; 162 | float startSeconds = CMTimeGetSeconds(timeRange.start); 163 | float durationSeconds = CMTimeGetSeconds(timeRange.duration); 164 | NSTimeInterval result = startSeconds + durationSeconds; 165 | 166 | return result; 167 | } 168 | 169 | // 获取当前的缓冲区域 170 | - (NSDictionary *)getLoadedTimeRange{ 171 | NSArray *loadedTimeRanges = [self.currentPlayerItem loadedTimeRanges]; 172 | CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; 173 | float startSeconds = CMTimeGetSeconds(timeRange.start); 174 | float durationSeconds = CMTimeGetSeconds(timeRange.duration); 175 | NSString *start = [NSString stringWithFormat:@"%.2f",startSeconds]; 176 | NSString *duration = [NSString stringWithFormat:@"%.2f",durationSeconds]; 177 | NSDictionary *timeRangeDic = @{@"start" : start, @"duration" : duration}; 178 | 179 | return timeRangeDic; 180 | } 181 | //根据toTime和缓冲区域数组判断是否显示菊花 182 | - (BOOL)judgeLoadedTimeIsShowActivity:(float)toTime{ 183 | BOOL show = YES; 184 | 185 | for (NSDictionary *timeRangeDic in self.loadedTimeRangeArr) { 186 | float start = [timeRangeDic[@"start"] floatValue]; 187 | float duration = [timeRangeDic[@"duration"] floatValue]; 188 | if (start < toTime && toTime < start + duration) { 189 | show = NO; 190 | break; 191 | } 192 | } 193 | return show; 194 | } 195 | //获取本地时间(秒) 196 | - (NSInteger)getLocalTime{ 197 | NSDate *date2 = [NSDate dateWithTimeIntervalSince1970:0]; 198 | double ss = [date2 timeIntervalSinceNow]; 199 | return fabs(ss); 200 | } 201 | //buffer值有时会不正常。所以只好这么处理 202 | - (void)handleBuffer{ 203 | 204 | if (self.playButtonState == YES) { 205 | 206 | if (self.isPlaying == NO) { 207 | 208 | if (self.currentBufferValue > self.lastBufferValue) { 209 | if ((self.currentBufferValue - self.lastBufferValue) > 5) { 210 | [self playForActivity]; 211 | } 212 | else if (self.currentBufferValue == self.duration){ 213 | [self playForActivity]; 214 | } 215 | else if ((int)(self.currentBufferValue + self.lastBufferValue+1) >= (int)self.duration){ 216 | [self playForActivity]; 217 | } 218 | } 219 | else{ 220 | if (self.currentBufferValue > 10) { 221 | [self playForActivity]; 222 | } 223 | else if (self.currentBufferValue == self.duration){ 224 | [self playForActivity]; 225 | } 226 | else if ((int)(self.currentBufferValue + self.duration + 1) >= (int)self.duration){ 227 | [self playForActivity]; 228 | } 229 | } 230 | } 231 | 232 | }else{ 233 | 234 | if (self.currentBufferValue > self.lastBufferValue) { 235 | if ((self.currentBufferValue - self.lastBufferValue) > 5) { 236 | [self delegateDidEndBuffer]; 237 | } 238 | else if (self.currentBufferValue == self.duration){ 239 | [self delegateDidEndBuffer]; 240 | } 241 | else if ((int)(self.currentBufferValue + self.lastBufferValue+1) >= (int)self.duration){ 242 | [self delegateDidEndBuffer]; 243 | } 244 | } 245 | else{ 246 | if (self.currentBufferValue > 10) { 247 | [self delegateDidEndBuffer]; 248 | } 249 | else if (self.currentBufferValue == self.duration){ 250 | [self delegateDidEndBuffer]; 251 | } 252 | else if ((int)(self.currentBufferValue + self.duration + 1) >= (int)self.duration){ 253 | [self delegateDidEndBuffer]; 254 | } 255 | } 256 | 257 | } 258 | } 259 | 260 | - (void)delegateDidStartBuffer{ 261 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidStartBuffer)]) { 262 | [_delegate videoPlayerDidStartBuffer]; 263 | } 264 | } 265 | - (void)delegateDidEndBuffer{ 266 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidEndBuffer)]) { 267 | [_delegate videoPlayerDidEndBuffer]; 268 | } 269 | } 270 | //播放(菊花显示逻辑) 271 | - (void)playForActivity{ 272 | if (self.playButtonState) { 273 | [self.player play]; 274 | } 275 | self.isBufferEmpty = NO; 276 | self.isPlaying = YES; 277 | [self delegateDidEndBuffer]; 278 | } 279 | //播放 280 | - (void)play{ 281 | if (self.playButtonState) { 282 | [self.player play]; 283 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidPlay)]) { 284 | [_delegate videoPlayerDidPlay]; 285 | } 286 | } 287 | } 288 | #pragma mark - Public Methods 289 | //播放(外部播放方法) 290 | - (void)playVideo{ 291 | if (_currentTime == _duration) { 292 | self.isPlaying = YES; 293 | self.playButtonState = YES; 294 | [self seekToTimePlay:0]; 295 | }else{ 296 | self.isPlaying = YES; 297 | self.playButtonState = YES; 298 | [self.player play]; 299 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidPlay)]) { 300 | [_delegate videoPlayerDidPlay]; 301 | } 302 | } 303 | } 304 | //暂停播放 305 | - (void)pauseVideo{ 306 | self.playButtonState = NO; 307 | [self.player pause]; 308 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidPause)]) { 309 | [_delegate videoPlayerDidPause]; 310 | } 311 | } 312 | 313 | //停止播放/清空播放器 314 | - (void)stopVideo{ 315 | 316 | if (!self.currentPlayerItem) return; 317 | [self.player pause]; 318 | [self.player cancelPendingPrerolls]; 319 | if (self.currentPlayerLayer) { 320 | [self.currentPlayerLayer removeFromSuperlayer]; 321 | } 322 | [self removeObserver]; 323 | self.player = nil; 324 | self.currentPlayerItem = nil; 325 | 326 | [self.loadedTimeRangeArr removeAllObjects]; 327 | self.loadedTimeRangeArr = nil; 328 | 329 | } 330 | 331 | #pragma mark - Observer 332 | - (void)addObserver { 333 | //监听播放进度 334 | __weak typeof(self) weakSelf = self; 335 | self.timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { 336 | CGFloat current = CMTimeGetSeconds(time); 337 | CGFloat total = CMTimeGetSeconds(weakSelf.currentPlayerItem.duration); 338 | _currentTime = current; 339 | 340 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(videoPlayer:totalTime:)]) { 341 | [weakSelf.delegate videoPlayer:weakSelf totalTime:total]; 342 | } 343 | 344 | if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(videoPlayer:currentTime:)]) { 345 | [weakSelf.delegate videoPlayer:weakSelf currentTime:current]; 346 | } 347 | 348 | /***** 这里是比较蛋疼的,当拖动滑块到没有缓冲的地方并且没有开始播放时,也会走到这里 *************/ 349 | if (weakSelf.isCanToGetLocalTime) { 350 | weakSelf.localTime = [weakSelf getLocalTime]; 351 | } 352 | NSInteger timeNow = [weakSelf getLocalTime]; 353 | if (timeNow - weakSelf.localTime > 1.5) { 354 | [weakSelf delegateDidEndBuffer]; 355 | weakSelf.isCanToGetLocalTime = YES; 356 | } 357 | }]; 358 | 359 | [self.currentPlayerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];//监听播放器的状态 360 | [self.currentPlayerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];//监听当前的缓冲进度 361 | [self.currentPlayerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];//监听到当前没有缓冲数据 362 | } 363 | 364 | - (void)removeObserver { 365 | if (self.timeObserve) { 366 | [self.player removeTimeObserver:self.timeObserve]; 367 | self.timeObserve = nil; 368 | } 369 | 370 | [self.currentPlayerItem removeObserver:self forKeyPath:@"status" context:nil]; 371 | [self.currentPlayerItem removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil]; 372 | [self.currentPlayerItem removeObserver:self forKeyPath:@"playbackBufferEmpty" context:nil]; 373 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 374 | [self.player replaceCurrentItemWithPlayerItem:nil]; 375 | } 376 | 377 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ 378 | 379 | AVPlayerItem *playerItem = (AVPlayerItem *)object; 380 | 381 | if ([keyPath isEqualToString:@"status"]) { 382 | AVPlayerItemStatus status = playerItem.status; 383 | switch (status) { 384 | case AVPlayerItemStatusUnknown:{ 385 | NSLog(@"======== 播放失败"); 386 | } 387 | break; 388 | 389 | case AVPlayerItemStatusReadyToPlay:{ 390 | self.player.muted = self.mute; 391 | [self play]; 392 | 393 | [self handleShowViewSublayers]; 394 | NSLog(@"========= 准备播放"); 395 | } 396 | break; 397 | 398 | case AVPlayerItemStatusFailed:{ 399 | NSLog(@"======== 播放失败"); 400 | } 401 | break; 402 | 403 | default: 404 | break; 405 | } 406 | } 407 | else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { 408 | NSTimeInterval current = [self availableDuration];// 计算缓冲进度 409 | 410 | CMTime duration = playerItem.duration; 411 | CGFloat totalDuration = CMTimeGetSeconds(duration); 412 | CGFloat progress = current / totalDuration; 413 | 414 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayer:bufferProgress:)]) { 415 | [_delegate videoPlayer:self bufferProgress:progress]; 416 | } 417 | 418 | self.duration = totalDuration; 419 | self.currentBufferValue = current; 420 | 421 | [self handleBuffer]; 422 | } 423 | else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { 424 | self.isPlaying = NO; 425 | self.isBufferEmpty = YES; 426 | self.lastBufferValue = self.currentBufferValue; 427 | 428 | [self delegateDidStartBuffer]; 429 | NSLog(@"====playbackBufferEmpty"); 430 | } 431 | } 432 | 433 | #pragma mark - NSNotification 434 | //播放结束 435 | - (void)playerItemDidPlayToEnd:(NSNotification *)notification{ 436 | 437 | //重新开始播放 438 | if (_replayWhenVideoPlayEnd) { 439 | __weak typeof(self) weak_self = self; 440 | [self.player seekToTime:CMTimeMake(0, 1) completionHandler:^(BOOL finished) { 441 | __strong typeof(weak_self) strong_self = weak_self; 442 | if (!strong_self) return; 443 | [strong_self.player play]; 444 | }]; 445 | }else{ 446 | [self pauseVideo]; 447 | } 448 | 449 | if (_delegate && [_delegate respondsToSelector:@selector(videoPlayerDidPlayToEnd)]) { 450 | [_delegate videoPlayerDidPlayToEnd]; 451 | } 452 | } 453 | //进入后台 454 | - (void)appDidEnterBackground{ 455 | if (self.stopWhenAppDidEnterBackground) { 456 | [self pauseVideo]; 457 | } 458 | } 459 | //进入前台 460 | - (void)appDidEnterForeground{ 461 | [self playVideo]; 462 | } 463 | 464 | @end 465 | 466 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYVideoPresenter.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPresenter.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 2018/5/8. 6 | // Copyright © 2018年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class LYVideoPresenter; 12 | @protocol LYVideoPresenterDelegate 13 | @optional 14 | - (void)videoPresenterDidBackBtnClick; 15 | @end 16 | 17 | @interface LYVideoPresenter : NSObject 18 | 19 | @property (nonatomic, weak) id delegate; 20 | 21 | ///根据url播放视频(初始化播放) 22 | - (void)playWithUrl:(NSString *)url addInView:(UIView *)view; 23 | 24 | /** 25 | 播放 26 | */ 27 | - (void)playVideo; 28 | 29 | /** 30 | 暂停 31 | */ 32 | - (void)pauseVideo; 33 | 34 | /** 35 | 停止播放/清空播放器 36 | */ 37 | - (void)stopVideo; 38 | 39 | /** 40 | @param toTime 从指定的时间开始播放(秒) 41 | */ 42 | - (void)seekToTimePlay:(float)toTime; 43 | 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/LYVideoPresenter.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPresenter.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 2018/5/8. 6 | // Copyright © 2018年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "LYVideoPresenter.h" 10 | #import "LYVideoPlayerControl.h" 11 | #import "LYVideoPlayer.h" 12 | 13 | @interface LYVideoPresenter () 14 | 15 | @property (nonatomic, strong) UIView *backgroundView; 16 | 17 | ///用于视频显示的View 18 | @property (nonatomic, strong) UIView *videoShowView; 19 | 20 | ///用于控制视频播放界面的View 21 | @property (nonatomic, strong) LYVideoPlayerControl *videoPlayControl; 22 | 23 | @property (nonatomic, strong) LYVideoPlayer *videoPlayer; 24 | 25 | ///是否为全屏 26 | @property (nonatomic, assign) BOOL isFullScreen; 27 | 28 | @property (nonatomic, assign) CGRect originFrame; 29 | 30 | @end 31 | 32 | @implementation LYVideoPresenter 33 | 34 | - (instancetype)init 35 | { 36 | self = [super init]; 37 | if (self) { 38 | [self addObserver]; 39 | } 40 | return self; 41 | } 42 | - (void)dealloc 43 | { 44 | [self removeObserver]; 45 | } 46 | 47 | //根据url播放视频(初始化播放) 48 | - (void)playWithUrl:(NSString *)url addInView:(UIView *)view{ 49 | 50 | self.backgroundView = view; 51 | self.originFrame = view.frame; 52 | 53 | ///播放器 54 | [self.videoPlayer playWithUrl:url showView:self.videoShowView]; 55 | 56 | ///控制界面 57 | [self videoPlayControl]; 58 | _videoPlayControl.halfScreenPanGestureEnabled = YES; 59 | } 60 | 61 | //监听通知 62 | - (void)addObserver{ 63 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeRotate:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; 64 | } 65 | //移除通知 66 | - (void)removeObserver{ 67 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 68 | } 69 | //横竖屏切换 70 | - (void)changeRotate:(NSNotification*)noti { 71 | 72 | if ([[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortrait 73 | || [[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortraitUpsideDown) { 74 | //竖屏 75 | _isFullScreen = NO; 76 | } else { 77 | //横屏 78 | _isFullScreen = YES; 79 | } 80 | 81 | [self autoSetDeviceRotate:[[UIDevice currentDevice] orientation]]; 82 | } 83 | 84 | //自动横竖屏切换 85 | - (void)autoSetDeviceRotate:(UIDeviceOrientation)orientation{ 86 | [UIView animateWithDuration:0.25 animations:^{ 87 | [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"]; 88 | [self setSubViewFrames]; 89 | }]; 90 | } 91 | 92 | //横竖屏切换 93 | - (void)setDeviceRotate{ 94 | UIDeviceOrientation orientation; 95 | if (_isFullScreen) { 96 | orientation = UIDeviceOrientationLandscapeLeft; 97 | }else{ 98 | orientation = UIDeviceOrientationPortrait; 99 | } 100 | 101 | [UIView animateWithDuration:0.25 animations:^{ 102 | [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"]; 103 | [self setSubViewFrames]; 104 | }]; 105 | } 106 | 107 | - (void)setSubViewFrames{ 108 | 109 | CGRect frame; 110 | if (_isFullScreen) { 111 | frame = [UIScreen mainScreen].bounds; 112 | }else{ 113 | frame = _originFrame; 114 | } 115 | 116 | self.backgroundView.frame = frame; 117 | 118 | [_videoPlayer fullScreenChanged:self.backgroundView.bounds]; 119 | [_videoPlayControl fullScreenChanged:_isFullScreen frame:self.backgroundView.bounds]; 120 | 121 | } 122 | 123 | #pragma mark - 懒加载 124 | - (LYVideoPlayer *)videoPlayer{ 125 | if (!_videoPlayer) { 126 | _videoPlayer = [[LYVideoPlayer alloc] init]; 127 | _videoPlayer.delegate = self; 128 | } 129 | return _videoPlayer; 130 | } 131 | - (UIView *)videoShowView{ 132 | if (!_videoShowView) { 133 | _videoShowView = [[UIView alloc] initWithFrame:self.backgroundView.bounds]; 134 | [self.backgroundView addSubview:_videoShowView]; 135 | } 136 | return _videoShowView; 137 | } 138 | 139 | - (LYVideoPlayerControl *)videoPlayControl{ 140 | if (!_videoPlayControl) { 141 | _videoPlayControl = [[LYVideoPlayerControl alloc] initWithFrame:self.backgroundView.bounds]; 142 | _videoPlayControl.delegate = self; 143 | [self.backgroundView addSubview:_videoPlayControl]; 144 | } 145 | return _videoPlayControl; 146 | } 147 | 148 | #pragma mark - Public Method 149 | /** 150 | 播放 151 | */ 152 | - (void)playVideo{ 153 | [_videoPlayer playVideo]; 154 | } 155 | 156 | /** 157 | 暂停 158 | */ 159 | - (void)pauseVideo{ 160 | [_videoPlayer pauseVideo]; 161 | } 162 | 163 | /** 164 | 停止播放/清空播放器 165 | */ 166 | - (void)stopVideo{ 167 | [_videoPlayer stopVideo]; 168 | } 169 | 170 | /** 171 | @param toTime 从指定的时间开始播放(秒) 172 | */ 173 | - (void)seekToTimePlay:(float)toTime{ 174 | [_videoPlayer seekToTimePlay:toTime]; 175 | } 176 | 177 | #pragma mark - LYVideoPlayerControlDelegate 178 | /** 179 | 返回按钮被点击 180 | */ 181 | - (void)videoControlDidBackBtnClick{ 182 | 183 | //全屏状态下,点击返回按钮,是置为竖屏 184 | if (_isFullScreen) { 185 | 186 | _isFullScreen = !_isFullScreen; 187 | [self setDeviceRotate]; 188 | 189 | }else{//竖屏状态下,点击返回按钮 190 | 191 | //首先要释放播放器 192 | [_videoPlayer stopVideo]; 193 | 194 | if (_delegate && [_delegate respondsToSelector:@selector(videoPresenterDidBackBtnClick)]) { 195 | [_delegate videoPresenterDidBackBtnClick]; 196 | } 197 | } 198 | } 199 | 200 | /** 201 | 全屏按钮被点击 202 | */ 203 | - (void)videoControlDidFullScreenBtnClick{ 204 | 205 | _isFullScreen = !_isFullScreen; 206 | 207 | [self setDeviceRotate]; 208 | } 209 | 210 | /** 211 | @param isPause YES:暂停 NO:播放 212 | */ 213 | - (void)videoControl:(LYVideoPlayerControl*)videoControl didPlayBtnIsPause:(BOOL)isPause{ 214 | if (isPause) { 215 | [_videoPlayer pauseVideo]; 216 | }else{ 217 | [_videoPlayer playVideo]; 218 | } 219 | } 220 | 221 | /** 222 | @param time 播放指定的时间(秒) 223 | */ 224 | - (void)videoControl:(LYVideoPlayerControl*)videoControl didPlayToTime:(CGFloat)time{ 225 | [_videoPlayer seekToTimePlay:time]; 226 | } 227 | 228 | #pragma mark - LYVideoPlayerDelegate 229 | /** 230 | 开始缓冲 231 | */ 232 | - (void)videoPlayerDidStartBuffer{ 233 | [_videoPlayControl videoPlayerDidLoading]; 234 | } 235 | /** 236 | 缓冲完成 237 | */ 238 | - (void)videoPlayerDidEndBuffer{ 239 | [_videoPlayControl videoPlayerDidBeginPlay]; 240 | } 241 | 242 | /** 243 | 播放器已经开始播放了 244 | */ 245 | - (void)videoPlayerDidPlay{ 246 | [_videoPlayControl playerControlPlay]; 247 | } 248 | /** 249 | 播放器已经暂停了播放 250 | */ 251 | - (void)videoPlayerDidPause{ 252 | [_videoPlayControl playerControlPause]; 253 | } 254 | 255 | /** 256 | @param totalTime 视频总长度(秒) 257 | */ 258 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer totalTime:(NSInteger)totalTime{ 259 | _videoPlayControl.totalTime = totalTime; 260 | } 261 | 262 | /** 263 | @param currentTime 当前播放进度(秒) 264 | */ 265 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer currentTime:(NSInteger)currentTime{ 266 | _videoPlayControl.currentTime = currentTime; 267 | _videoPlayControl.playValue = (CGFloat)_videoPlayControl.currentTime/(CGFloat)_videoPlayControl.totalTime; 268 | } 269 | 270 | /** 271 | @param bufferProgress 当前缓冲进度比例(0~1) 272 | */ 273 | - (void)videoPlayer:(LYVideoPlayer *)videoPlayer bufferProgress:(CGFloat)bufferProgress{ 274 | _videoPlayControl.progress = bufferProgress; 275 | } 276 | 277 | @end 278 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/category/NSString+LYFileHandlePath.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LYFileHandlePath.h 3 | // LYURLSession 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.xmpp_test. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (LYFileHandlePath) 12 | 13 | 14 | /** 临时文件路径 */ 15 | + (NSString *)tempFilePathWithFileName:(NSString *)name; 16 | /** 临时文件路径 */ 17 | + (NSString *)tempFilePathWithUrlString:(NSString *)urlString; 18 | 19 | /** 缓存文件夹路径 */ 20 | + (NSString *)cacheFilePathWithName:(NSString *)name; 21 | /** 缓存文件夹路径 */ 22 | + (NSString *)cacheFilePathWithUrlString:(NSString *)urlString; 23 | 24 | /** 获取网址中的文件名 */ 25 | + (NSString *)fileNameWithUrlString:(NSString *)url; 26 | 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/category/NSString+LYFileHandlePath.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+LYFileHandlePath.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #import "NSString+LYFileHandlePath.h" 11 | 12 | @implementation NSString (LYFileHandlePath) 13 | 14 | + (NSString *)tempFilePathWithFileName:(NSString *)name { 15 | return [[NSHomeDirectory( ) stringByAppendingPathComponent:@"tmp"] stringByAppendingPathComponent:name]; 16 | } 17 | 18 | + (NSString *)tempFilePathWithUrlString:(NSString *)urlString{ 19 | NSString *name = [[urlString componentsSeparatedByString:@"/"] lastObject]; 20 | return [[NSHomeDirectory( ) stringByAppendingPathComponent:@"tmp"] stringByAppendingPathComponent:name]; 21 | } 22 | 23 | + (NSString *)cacheFilePathWithName:(NSString *)name{ 24 | NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; 25 | NSString *cacheFolderPath = [cachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"/Moment_Videos/%@",name]]; 26 | return cacheFolderPath; 27 | } 28 | 29 | + (NSString *)cacheFilePathWithUrlString:(NSString *)urlString{ 30 | NSString *name = [[urlString componentsSeparatedByString:@"/"] lastObject]; 31 | NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; 32 | NSString *cacheFolderPath = [cachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"/Moment_Videos/%@",name]]; 33 | return cacheFolderPath; 34 | } 35 | 36 | + (NSString *)fileNameWithUrlString:(NSString *)url{ 37 | return [[url componentsSeparatedByString:@"/"] lastObject]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/category/UIImage+ComPress.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+ComPress.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/6. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | 12 | @interface UIImage (ComPress) 13 | 14 | /** 缩放到指定大小 */ 15 | - (UIImage*)imageCompressWithSimple:(UIImage*)image scaledToSize:(CGSize)size; 16 | 17 | /** 根据颜色和圆的半径来创建一个 Image */ 18 | + (UIImage *)createImageWithColor:(UIColor *)color radius:(CGFloat)radius; 19 | 20 | /** 根据一个view来创建一个 Image */ 21 | + (UIImage*)creatImageWithView:(UIView *)theView; 22 | @end 23 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/category/UIImage+ComPress.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+ComPress.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/6. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | 10 | #import "UIImage+ComPress.h" 11 | #define PI 3.14159265358979323846 12 | 13 | @implementation UIImage (ComPress) 14 | 15 | //缩放到指定大小 16 | - (UIImage*)imageCompressWithSimple:(UIImage*)image scaledToSize:(CGSize)size 17 | { 18 | UIGraphicsBeginImageContext(size); 19 | [image drawInRect:CGRectMake(0,0,size.width,size.height)]; 20 | UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 21 | UIGraphicsEndImageContext(); 22 | return newImage; 23 | } 24 | 25 | //根据颜色和圆的半径来创建一个圆形图片 26 | + (UIImage *)createImageWithColor:(UIColor *)color radius:(CGFloat)radius 27 | { 28 | CGRect rect = CGRectMake(0.0f, 0.0f,radius * 2 + 4, radius * 2 + 4); 29 | 30 | UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); 31 | CGContextRef context =UIGraphicsGetCurrentContext(); 32 | CGContextSetRGBStrokeColor(context,1,1,1,1.0);//画笔线的颜色 33 | CGContextSetFillColorWithColor(context, color.CGColor);//填充颜色 34 | CGContextSetLineWidth(context, 4.0);//线的宽度 35 | CGContextAddArc(context, radius + 2,radius + 2, radius, 0, 2*PI, 0); //添加一个圆 36 | CGContextDrawPath(context, kCGPathFillStroke); //绘制路径加填充 37 | 38 | UIImage *myImage =UIGraphicsGetImageFromCurrentImageContext(); 39 | UIGraphicsEndImageContext(); 40 | 41 | return myImage; 42 | } 43 | 44 | //根据一个view来创建一个 Image 45 | + (UIImage*)creatImageWithView:(UIView *)theView 46 | { 47 | CGRect rect = theView.frame; 48 | UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0); 49 | CGContextRef context = UIGraphicsGetCurrentContext(); 50 | [theView.layer renderInContext:context]; 51 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 52 | UIGraphicsEndImageContext(); 53 | 54 | return img; 55 | 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/playControlView/LYSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYSlider.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LYSlider : UIControl 12 | 13 | 14 | @property (nonatomic, assign) CGFloat value; //0 - 1. 播放进度 15 | @property (nonatomic, assign) CGFloat bufferProgress; //0 - 1. 缓冲进度 16 | 17 | @property (nonatomic, assign) CGFloat trackHeight; //轨道高度 18 | 19 | @property (nonatomic, assign) CGFloat thumbTouchSize; //滑块触发大小的宽高 20 | @property (nonatomic, assign) CGFloat thumbVisibleSize; //滑块可视大小的宽高 21 | 22 | @property (nonatomic, strong) UIColor *trackColor; //轨道的颜色 23 | @property (nonatomic, strong) UIColor *bufferColor; //缓冲的颜色 24 | @property (nonatomic, strong) UIColor *thumbValueColor; //播放进度的颜色 25 | 26 | /** 可为滑块设置图片 */ 27 | - (void)setThumbImage:(UIImage *)thumbImage forState:(UIControlState)state; 28 | 29 | //横竖屏转换 30 | - (void)fullScreenChanged:(BOOL)isFullScreen; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/playControlView/LYSlider.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYSlider.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "LYSlider.h" 10 | 11 | @interface LYSlider () 12 | 13 | @property (nonatomic, strong) UIImageView *trackImageView; //缓冲轨道 14 | @property (nonatomic, strong) UIImageView *bufferImageView;//缓冲进度 15 | @property (nonatomic, strong) UIImageView *thumbValueImageView;//滑块进度 16 | 17 | @property (nonatomic, strong) UIView *thumb; //滑块的父视图(不可见) 18 | @property (nonatomic, strong) UIImageView *thumbImageView; //用于显示滑块的ImageView(可视) 19 | 20 | @end 21 | 22 | @implementation LYSlider 23 | { 24 | CGRect _frame; 25 | } 26 | 27 | - (instancetype)initWithFrame:(CGRect)frame 28 | { 29 | self = [super initWithFrame:frame]; 30 | if (self) { 31 | self.backgroundColor = [UIColor clearColor]; 32 | _frame = frame; 33 | _thumbTouchSize = _frame.size.height; 34 | _thumbVisibleSize = 10; 35 | _trackHeight = 2; 36 | 37 | self.trackImageView.backgroundColor = [UIColor grayColor]; 38 | self.bufferImageView.backgroundColor = [UIColor whiteColor]; 39 | self.thumbValueImageView.backgroundColor = [UIColor redColor]; 40 | self.thumb.backgroundColor = [UIColor clearColor]; 41 | self.thumbImageView.backgroundColor = [UIColor whiteColor]; 42 | 43 | [self creatUI]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)creatUI{ 49 | 50 | self.trackImageView.frame = CGRectMake(0, (_frame.size.height - _trackHeight) * 0.5, _frame.size.width, _trackHeight); 51 | self.bufferImageView.frame = CGRectMake(0, (_frame.size.height - _trackHeight) * 0.5, _bufferProgress * _frame.size.width, _trackHeight); 52 | 53 | self.thumbValueImageView.frame = CGRectMake(0, (_frame.size.height - _trackHeight) * 0.5, _value * _frame.size.width, _trackHeight); 54 | self.thumb.frame = CGRectMake(0, 0, _thumbTouchSize, _thumbTouchSize); 55 | self.thumb.center = [self getThumbCenterWithValue:_value]; 56 | self.thumbImageView.frame = CGRectMake((_thumbTouchSize - _thumbVisibleSize) * 0.5, (_thumbTouchSize - _thumbVisibleSize) * 0.5, _thumbVisibleSize, _thumbVisibleSize); 57 | } 58 | - (CGPoint)getThumbCenterWithValue:(CGFloat)value{ 59 | CGFloat thumbX = _thumbVisibleSize * 0.5 + (_frame.size.width - _thumbVisibleSize) * value; 60 | CGFloat thumbY = _frame.size.height * 0.5; 61 | return CGPointMake(thumbX, thumbY); 62 | } 63 | 64 | - (UIImageView *)trackImageView{ 65 | if (!_trackImageView) { 66 | _trackImageView = [[UIImageView alloc] init]; 67 | _trackImageView.layer.masksToBounds = YES; 68 | [self addSubview:_trackImageView]; 69 | } 70 | return _trackImageView; 71 | } 72 | 73 | - (UIImageView *)bufferImageView{ 74 | if (!_bufferImageView) { 75 | _bufferImageView = [[UIImageView alloc] init]; 76 | _bufferImageView.layer.masksToBounds = YES; 77 | [self addSubview:_bufferImageView]; 78 | } 79 | return _bufferImageView; 80 | } 81 | 82 | - (UIImageView *)thumbValueImageView{ 83 | if (!_thumbValueImageView) { 84 | _thumbValueImageView = [[UIImageView alloc] init]; 85 | _thumbValueImageView.layer.masksToBounds = YES; 86 | [self addSubview:_thumbValueImageView]; 87 | } 88 | return _thumbValueImageView; 89 | } 90 | 91 | - (UIView *)thumb{ 92 | if (!_thumb) { 93 | _thumb = [[UIView alloc] init]; 94 | _thumb.layer.masksToBounds = YES; 95 | _thumb.userInteractionEnabled = NO; 96 | [self addSubview:_thumb]; 97 | } 98 | return _thumb; 99 | } 100 | 101 | - (UIImageView *)thumbImageView{ 102 | if (!_thumbImageView) { 103 | _thumbImageView = [[UIImageView alloc] init]; 104 | _thumbImageView.layer.masksToBounds = YES; 105 | [self.thumb addSubview:_thumbImageView]; 106 | } 107 | return _thumbImageView; 108 | } 109 | - (void)setTrackColor:(UIColor *)trackColor{ 110 | self.trackImageView.backgroundColor = trackColor; 111 | } 112 | - (void)setBufferColor:(UIColor *)bufferColor{ 113 | self.bufferImageView.backgroundColor = bufferColor; 114 | } 115 | - (void)setThumbValueColor:(UIColor *)thumbValueColor{ 116 | self.thumbImageView.backgroundColor = thumbValueColor; 117 | } 118 | - (void)setTrackHeight:(CGFloat)trackHeight{ 119 | _trackHeight = trackHeight; 120 | [self creatUI]; 121 | } 122 | - (void)setThumbVisibleSize:(CGFloat)thumbVisibleSize{ 123 | _thumbVisibleSize = thumbVisibleSize; 124 | [self creatUI]; 125 | } 126 | - (void)setBufferProgress:(CGFloat)bufferProgress{ 127 | 128 | bufferProgress = [self valid:bufferProgress]; 129 | if (_bufferProgress == bufferProgress) { 130 | return; 131 | } 132 | _bufferProgress = bufferProgress; 133 | self.bufferImageView.frame = CGRectMake(0, (_frame.size.height - _trackHeight) * 0.5, _bufferProgress * _frame.size.width, _trackHeight); 134 | } 135 | - (void)setValue:(CGFloat)value { 136 | 137 | value = [self valid:value]; 138 | if (_value == value) { 139 | return; 140 | } 141 | _value = value; 142 | 143 | self.thumb.center = [self getThumbCenterWithValue:_value]; 144 | self.thumbValueImageView.frame = CGRectMake(0, (_frame.size.height - _trackHeight) * 0.5, _value * _frame.size.width, _trackHeight); 145 | } 146 | 147 | - (float)valid:(float)f { 148 | if (isnan(f)) { 149 | return 0.0; 150 | } 151 | if (f < 0.005) { 152 | return 0.0; 153 | } 154 | else if (f > 0.995) { 155 | f = 1.0; 156 | } 157 | return f; 158 | } 159 | 160 | - (void)setThumbImage:(UIImage *)thumbImage forState:(UIControlState)state{ 161 | if (state == UIControlStateNormal) { 162 | self.thumbImageView.image = thumbImage; 163 | self.thumbImageView.backgroundColor = [UIColor clearColor]; 164 | } 165 | else if (state == UIControlStateHighlighted) { 166 | self.thumbImageView.highlightedImage = thumbImage; 167 | self.thumbImageView.backgroundColor = [UIColor clearColor]; 168 | } 169 | } 170 | //横竖屏转换 171 | - (void)fullScreenChanged:(BOOL)isFullScreen{ 172 | _frame = self.bounds; 173 | [self creatUI]; 174 | } 175 | 176 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 177 | CGPoint location = [touch locationInView:self]; 178 | if (!CGRectContainsPoint(self.thumb.frame, location)) { 179 | return NO; 180 | } 181 | self.thumbImageView.highlighted = YES; 182 | [self sendActionsForControlEvents:UIControlEventEditingDidBegin]; 183 | return YES; 184 | } 185 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 186 | CGPoint location = [touch locationInView:self]; 187 | 188 | if (location.x <= CGRectGetWidth(self.bounds) + 10 && location.x >= - 10) { 189 | self.thumbImageView.highlighted = YES; 190 | self.value = location.x / CGRectGetWidth(self.bounds); 191 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 192 | } 193 | return YES; 194 | } 195 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 196 | self.thumbImageView.highlighted = NO; 197 | [self sendActionsForControlEvents:UIControlEventEditingDidEnd]; 198 | } 199 | 200 | @end 201 | 202 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/playControlView/LYVideoPlayerControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayerControl.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "LYSlider.h" 12 | 13 | @class LYVideoPlayerControl; 14 | @protocol LYVideoPlayerControlDelegate 15 | @optional 16 | 17 | /** 18 | 返回按钮被点击 19 | */ 20 | - (void)videoControlDidBackBtnClick; 21 | 22 | /** 23 | 全屏按钮被点击 24 | */ 25 | - (void)videoControlDidFullScreenBtnClick; 26 | 27 | /** 28 | @param isPause YES:暂停 NO:播放 29 | */ 30 | - (void)videoControl:(LYVideoPlayerControl*)videoControl didPlayBtnIsPause:(BOOL)isPause; 31 | 32 | /** 33 | @param time 播放指定的时间(秒) 34 | */ 35 | - (void)videoControl:(LYVideoPlayerControl*)videoControl didPlayToTime:(CGFloat)time; 36 | 37 | @end 38 | 39 | @interface LYVideoPlayerControl : UIView 40 | 41 | @property (nonatomic, weak) id delegate; 42 | 43 | @property (nonatomic, assign) NSInteger currentTime; 44 | @property (nonatomic, assign) NSInteger totalTime; 45 | @property (nonatomic, assign) CGFloat playValue; //播放进度 46 | @property (nonatomic, assign) CGFloat progress; //缓冲进度 47 | ///半屏播放时,滑动手势在当前view(控制面板)唤起时是否可用 48 | @property (nonatomic, assign) BOOL halfScreenPanGestureEnabled; 49 | 50 | //播放器调用方法 51 | - (void)videoPlayerDidLoading; 52 | 53 | - (void)videoPlayerDidBeginPlay; 54 | 55 | - (void)videoPlayerDidEndPlay; 56 | 57 | - (void)videoPlayerDidFailedPlay; 58 | 59 | //外部方法播放 60 | - (void)playerControlPlay; 61 | //外部方法暂停 62 | - (void)playerControlPause; 63 | 64 | //横竖屏转换 65 | - (void)fullScreenChanged:(BOOL)isFullScreen frame:(CGRect)frame; 66 | 67 | @end 68 | 69 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/playControlView/LYVideoPlayerControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYVideoPlayerControl.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/5. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "LYVideoPlayerControl.h" 10 | #import "UIImage+ComPress.h" 11 | 12 | #define TopHeight 40 13 | #define BottomHeight 40 14 | 15 | #define PlayButotn_playImage @"video_play" 16 | #define PlayButotn_pasueImage @"video_pause" 17 | 18 | @interface LYVideoPlayerControl () 19 | 20 | //全屏 21 | @property (nonatomic, strong) UIView *fullScreenView; //全屏的一个视图 22 | @property (nonatomic, strong) UILabel *fastTimeLabel; //全屏显示快进快退时的时间进度 23 | @property (nonatomic, strong) UIActivityIndicatorView *activityView; //菊花 24 | @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; //滑动手势 25 | @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; //单击手势 26 | 27 | //顶部背景视图 28 | @property (nonatomic, strong) UIView *topView; 29 | @property (nonatomic, strong) UIButton *backButton; //返回 30 | @property (nonatomic, strong) UIButton *fullScreenButton;//全屏按钮 31 | 32 | //底部背景视图 33 | @property (nonatomic, strong) UIView *bottomView; 34 | @property (nonatomic, strong) UIButton *playButton; //播放/暂停 35 | @property (nonatomic, strong) UILabel *currentLabel; //当前播放时间 36 | @property (nonatomic, strong) UILabel *totalLabel; //视频总时间 37 | 38 | @property (nonatomic, strong) LYSlider *videoSlider; //滑动条 39 | 40 | @property (nonatomic, strong) MPVolumeView *volumeView; //系统音量控件 41 | @property (strong, nonatomic) UISlider* volumeViewSlider;//控制音量 42 | 43 | @property (nonatomic, strong) NSTimer *hideControlTimer;//隐藏控制view的定时器 44 | 45 | @end 46 | 47 | @implementation LYVideoPlayerControl 48 | { 49 | CGRect _frame; 50 | BOOL _isShowControl;//控制界面是否显示 YES:显示 NO:隐藏 51 | 52 | BOOL _sliderIsTouching;//slider是否正在滑动 53 | CGPoint _startPoint; //手势滑动的起始点 54 | CGPoint _lastPoint; //记录上次滑动的点 55 | BOOL _isStartPan; //记录手势开始滑动 56 | CGFloat _fastCurrentTime;//记录当前快进快退的时间 57 | } 58 | - (instancetype)initWithFrame:(CGRect)frame 59 | { 60 | self = [super initWithFrame:frame]; 61 | if (self) { 62 | _frame = frame; 63 | self.layer.masksToBounds = YES; 64 | [self creatUI]; 65 | } 66 | return self; 67 | } 68 | - (void)creatUI{ 69 | _isShowControl = YES; 70 | 71 | self.volumeView.frame = self.bounds; 72 | 73 | //全屏的东西 74 | self.fullScreenView.frame = self.bounds; 75 | self.fastTimeLabel.frame = self.bounds; 76 | self.activityView.frame = self.bounds; 77 | //手势 78 | [self.fullScreenView addGestureRecognizer:self.tapGesture]; 79 | [self.fullScreenView addGestureRecognizer:self.panGesture]; 80 | 81 | //顶部 82 | self.topView.frame = CGRectMake(0, 0, _frame.size.width, TopHeight); 83 | self.backButton.frame = CGRectMake(0, 0, 40, TopHeight); 84 | self.fullScreenButton.frame = CGRectMake(_frame.size.width - 40, 0, 40, TopHeight); 85 | 86 | //低部 87 | self.bottomView.frame = CGRectMake(0, _frame.size.height - BottomHeight, _frame.size.width, BottomHeight); 88 | self.playButton.frame = CGRectMake(0, 0, 40, BottomHeight); 89 | self.currentLabel.frame = CGRectMake(CGRectGetMaxX(self.playButton.frame), 0, 50, BottomHeight); 90 | self.totalLabel.frame = CGRectMake(_frame.size.width - 50 - 10, 0, 50, BottomHeight); 91 | 92 | self.videoSlider.frame = CGRectMake(CGRectGetMaxX(self.currentLabel.frame) + 5, 0, _frame.size.width - CGRectGetMaxX(self.currentLabel.frame) - self.totalLabel.frame.size.width - 20 , BottomHeight); 93 | } 94 | 95 | #pragma mark - set get 96 | //全屏 97 | - (UIView *)fullScreenView{ 98 | if (!_fullScreenView) { 99 | _fullScreenView = [[UIView alloc] init]; 100 | [self addSubview:_fullScreenView]; 101 | } 102 | return _fullScreenView; 103 | } 104 | - (UILabel *)fastTimeLabel{ 105 | if (!_fastTimeLabel) { 106 | _fastTimeLabel = [[UILabel alloc] init]; 107 | _fastTimeLabel.textColor = [UIColor whiteColor]; 108 | _fastTimeLabel.font = [UIFont systemFontOfSize:30]; 109 | _fastTimeLabel.textAlignment = NSTextAlignmentCenter; 110 | _fastTimeLabel.hidden = YES; 111 | [self.fullScreenView addSubview:_fastTimeLabel]; 112 | } 113 | return _fastTimeLabel; 114 | } 115 | - (UIActivityIndicatorView *)activityView{ 116 | if (!_activityView) { 117 | _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; 118 | _activityView.hidesWhenStopped = YES; 119 | [self.fullScreenView addSubview:_activityView]; 120 | [_activityView startAnimating]; 121 | } 122 | return _activityView; 123 | } 124 | - (UITapGestureRecognizer *)tapGesture{ 125 | if (!_tapGesture) { 126 | _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureTouch:)]; 127 | _tapGesture.delegate = self; 128 | } 129 | return _tapGesture; 130 | } 131 | - (UIPanGestureRecognizer *)panGesture{ 132 | if (!_panGesture) { 133 | _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureTouch:)]; 134 | } 135 | return _panGesture; 136 | } 137 | //顶部 138 | - (UIView *)topView{ 139 | if (!_topView) { 140 | _topView = [[UIView alloc] init]; 141 | _topView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 142 | [self addSubview:_topView]; 143 | } 144 | return _topView; 145 | } 146 | - (UIButton *)backButton{ 147 | if (!_backButton) { 148 | _backButton = [UIButton buttonWithType:UIButtonTypeCustom]; 149 | [_backButton setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; 150 | [_backButton addTarget:self action:@selector(backButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 151 | [self.topView addSubview:_backButton]; 152 | } 153 | return _backButton; 154 | } 155 | - (UIButton *)fullScreenButton{ 156 | if (!_fullScreenButton) { 157 | _fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom]; 158 | [_fullScreenButton setImage:[UIImage imageNamed:@"normal_screen"] forState:UIControlStateNormal]; 159 | [_fullScreenButton setImage:[UIImage imageNamed:@"full_screen"] forState:UIControlStateSelected]; 160 | [_fullScreenButton addTarget:self action:@selector(fullScreenButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 161 | [self.topView addSubview:_fullScreenButton]; 162 | } 163 | return _fullScreenButton; 164 | } 165 | //底部背景视图 166 | - (UIView *)bottomView{ 167 | if (!_bottomView) { 168 | _bottomView = [[UIView alloc] init]; 169 | _bottomView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 170 | [self addSubview:_bottomView]; 171 | } 172 | return _bottomView; 173 | } 174 | - (UIButton *)playButton{ 175 | if (!_playButton) { 176 | _playButton = [UIButton buttonWithType:UIButtonTypeCustom]; 177 | [_playButton setImage:[UIImage imageNamed:PlayButotn_pasueImage] forState:UIControlStateNormal]; 178 | [_playButton setImage:[UIImage imageNamed:PlayButotn_playImage] forState:UIControlStateSelected]; 179 | [_playButton addTarget:self action:@selector(playButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 180 | [self.bottomView addSubview:_playButton]; 181 | } 182 | return _playButton; 183 | } 184 | - (UILabel *)currentLabel{ 185 | if (!_currentLabel) { 186 | _currentLabel = [[UILabel alloc] init]; 187 | _currentLabel.text = @"00:00"; 188 | _currentLabel.textColor = [UIColor whiteColor]; 189 | _currentLabel.textAlignment = NSTextAlignmentCenter; 190 | _currentLabel.font = [UIFont systemFontOfSize:14]; 191 | [self.bottomView addSubview:_currentLabel]; 192 | } 193 | return _currentLabel; 194 | } 195 | - (UILabel *)totalLabel{ 196 | if (!_totalLabel) { 197 | _totalLabel = [[UILabel alloc] init]; 198 | _totalLabel.text = @"00:00"; 199 | _totalLabel.textColor = [UIColor whiteColor]; 200 | _totalLabel.textAlignment = NSTextAlignmentCenter; 201 | _totalLabel.font = [UIFont systemFontOfSize:14]; 202 | [self.bottomView addSubview:_totalLabel]; 203 | } 204 | return _totalLabel; 205 | } 206 | 207 | - (LYSlider *)videoSlider{ 208 | if (!_videoSlider) { 209 | _videoSlider = [[LYSlider alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.currentLabel.frame) + 5, 0, _frame.size.width - CGRectGetMaxX(self.currentLabel.frame) - self.totalLabel.frame.size.width - 20 , BottomHeight)]; 210 | 211 | //设置滑块图片样式 212 | // 1 通过颜色创建 Image 213 | UIImage *normalImage = [UIImage createImageWithColor:[UIColor redColor] radius:5.0]; 214 | 215 | // UIView *normalImageView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 8)]; 216 | // normalImageView.layer.cornerRadius = 1; 217 | // normalImageView.layer.masksToBounds = YES; 218 | // normalImageView.backgroundColor = [UIColor whiteColor]; 219 | // UIImage *normalImage = [UIImage creatImageWithView:normalImageView]; 220 | 221 | // 2 通过view 创建 Image 222 | UIView *highlightView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)]; 223 | highlightView.layer.cornerRadius = 6; 224 | highlightView.layer.masksToBounds = YES; 225 | highlightView.backgroundColor = [UIColor redColor]; 226 | UIImage *highlightImage = [UIImage creatImageWithView:highlightView]; 227 | 228 | [_videoSlider setThumbImage:normalImage forState:UIControlStateNormal]; 229 | [_videoSlider setThumbImage:highlightImage forState:UIControlStateHighlighted]; 230 | 231 | _videoSlider.trackHeight = 1.5; 232 | _videoSlider.thumbVisibleSize = 12;//设置滑块(可见的)大小 233 | 234 | [_videoSlider addTarget:self action:@selector(sliderValueChange:) forControlEvents:UIControlEventValueChanged];//正在拖动 235 | [_videoSlider addTarget:self action:@selector(sliderTouchEnd:) forControlEvents:UIControlEventEditingDidEnd];//拖动结束 236 | [self.bottomView addSubview:_videoSlider]; 237 | } 238 | return _videoSlider; 239 | } 240 | - (MPVolumeView *)volumeView { 241 | if (_volumeView == nil) { 242 | _volumeView = [[MPVolumeView alloc] init]; 243 | [_volumeView sizeToFit]; 244 | for (UIView *view in [_volumeView subviews]){ 245 | if ([view.class.description isEqualToString:@"MPVolumeSlider"]){ 246 | self.volumeViewSlider = (UISlider*)view; 247 | break; 248 | } 249 | } 250 | } 251 | return _volumeView; 252 | } 253 | 254 | - (void)setTotalTime:(NSInteger)totalTime{ 255 | _totalTime = totalTime; 256 | self.totalLabel.text = [self timeFormatted:totalTime]; 257 | } 258 | - (void)setCurrentTime:(NSInteger)currentTime{ 259 | _currentTime = currentTime; 260 | if (_sliderIsTouching == NO) { 261 | self.currentLabel.text = [self timeFormatted:currentTime]; 262 | } 263 | } 264 | - (void)setPlayValue:(CGFloat)playValue{ 265 | _playValue = playValue; 266 | if (_sliderIsTouching == NO) { 267 | self.videoSlider.value = playValue; 268 | } 269 | } 270 | - (void)setProgress:(CGFloat)progress{ 271 | _progress = progress; 272 | self.videoSlider.bufferProgress = progress; 273 | } 274 | 275 | #pragma mark - Event 276 | - (void)sliderValueChange:(LYSlider *)slider{ 277 | _sliderIsTouching = YES; 278 | self.currentLabel.text = [self timeFormatted:slider.value * self.totalTime]; 279 | [self startHideControlTimer]; 280 | } 281 | - (void)sliderTouchEnd:(LYSlider *)slider{ 282 | 283 | if (_delegate && [_delegate respondsToSelector:@selector(videoControl:didPlayToTime:)]) { 284 | [_delegate videoControl:self didPlayToTime:slider.value * self.totalTime]; 285 | } 286 | _sliderIsTouching = NO; 287 | [self startHideControlTimer]; 288 | } 289 | - (void)playButtonClick:(UIButton *)sender{ 290 | [self startHideControlTimer]; 291 | if (_delegate && [_delegate respondsToSelector:@selector(videoControl:didPlayBtnIsPause:)]) { 292 | [_delegate videoControl:self didPlayBtnIsPause:!sender.selected]; 293 | } 294 | } 295 | - (void)backButtonClick:(UIButton *)sender{ 296 | [self startHideControlTimer]; 297 | if (_delegate && [_delegate respondsToSelector:@selector(videoControlDidBackBtnClick)]) { 298 | [_delegate videoControlDidBackBtnClick]; 299 | } 300 | } 301 | - (void)fullScreenButtonClick:(UIButton *)sender{ 302 | [self startHideControlTimer]; 303 | if (_delegate && [_delegate respondsToSelector:@selector(videoControlDidFullScreenBtnClick)]) { 304 | [_delegate videoControlDidFullScreenBtnClick]; 305 | } 306 | } 307 | - (void)tapGestureTouch:(UITapGestureRecognizer *)tapGesture{ 308 | _isShowControl = !_isShowControl; 309 | [self showOrHideControlView]; 310 | } 311 | - (void)panGestureTouch:(UIPanGestureRecognizer *)panGestureTouch{ 312 | CGPoint touPoint = [panGestureTouch translationInView:self]; 313 | static int changeXorY = 0; //0:X:进度 1:Y:音量 314 | 315 | if (panGestureTouch.state == UIGestureRecognizerStateBegan) { 316 | _startPoint = touPoint; 317 | _lastPoint = touPoint; 318 | _isStartPan = YES; 319 | _fastCurrentTime = self.currentTime; 320 | changeXorY = 0; 321 | }else if (panGestureTouch.state == UIGestureRecognizerStateChanged){ 322 | CGFloat change_X = touPoint.x - _startPoint.x; 323 | CGFloat change_Y = touPoint.y - _startPoint.y; 324 | 325 | if (_isStartPan) { 326 | 327 | if (fabs(change_X) > fabs(change_Y)) { 328 | changeXorY = 0; 329 | }else{ 330 | changeXorY = 1; 331 | } 332 | _isStartPan = NO; 333 | } 334 | if (changeXorY == 0) {//进度 335 | self.fastTimeLabel.hidden = NO; 336 | 337 | if (touPoint.x - _lastPoint.x >= 1) { 338 | _lastPoint = touPoint; 339 | _fastCurrentTime += 2; 340 | if (_fastCurrentTime > self.totalTime) { 341 | _fastCurrentTime = self.totalTime; 342 | } 343 | } 344 | if (touPoint.x - _lastPoint.x <= - 1) { 345 | _lastPoint = touPoint; 346 | _fastCurrentTime -= 2; 347 | if (_fastCurrentTime < 0) { 348 | _fastCurrentTime = 0; 349 | } 350 | } 351 | 352 | NSString *currentTimeString = [self timeFormatted:(int)_fastCurrentTime]; 353 | NSString *totalTimeString = [self timeFormatted:(int)self.totalTime]; 354 | self.fastTimeLabel.text = [NSString stringWithFormat:@"%@/%@",currentTimeString,totalTimeString]; 355 | 356 | }else{//音量 357 | if (touPoint.y - _lastPoint.y >= 5) { 358 | _lastPoint = touPoint; 359 | self.volumeViewSlider.value -= 0.07; 360 | } 361 | if (touPoint.y - _lastPoint.y <= - 5) { 362 | _lastPoint = touPoint; 363 | self.volumeViewSlider.value += 0.07; 364 | } 365 | } 366 | 367 | }else if (panGestureTouch.state == UIGestureRecognizerStateEnded){ 368 | self.fastTimeLabel.hidden = YES; 369 | if (changeXorY == 0) { 370 | if (_delegate && [_delegate respondsToSelector:@selector(videoControl:didPlayToTime:)]) { 371 | [_delegate videoControl:self didPlayToTime:_fastCurrentTime]; 372 | } 373 | } 374 | [self startHideControlTimer]; 375 | } 376 | } 377 | 378 | #pragma mark - Public Methods 379 | //横竖屏转换 380 | - (void)fullScreenChanged:(BOOL)isFullScreen frame:(CGRect)frame{ 381 | self.frame = frame; 382 | _frame = frame; 383 | [self creatUI]; 384 | 385 | self.fullScreenButton.selected = isFullScreen; 386 | 387 | [self.videoSlider fullScreenChanged:isFullScreen]; 388 | } 389 | - (void)videoPlayerDidLoading{ 390 | [self.activityView startAnimating]; 391 | } 392 | - (void)videoPlayerDidBeginPlay{ 393 | [self.activityView stopAnimating]; 394 | [self startHideControlTimer]; 395 | } 396 | - (void)videoPlayerDidEndPlay{ 397 | // NSLog(@"播放结束"); 398 | } 399 | - (void)videoPlayerDidFailedPlay{ 400 | // NSLog(@"播放失败"); 401 | } 402 | 403 | - (void)playerControlPlay{ 404 | self.playButton.selected = NO; 405 | [self startHideControlTimer]; 406 | } 407 | - (void)playerControlPause{ 408 | self.playButton.selected = YES; 409 | //暂停时显示控制面板 410 | _isShowControl = YES; 411 | [self showOrHideControlView]; 412 | } 413 | 414 | #pragma mark - Private Method 415 | - (void)startHideControlTimer{ 416 | 417 | //销毁定时器 418 | [_hideControlTimer invalidate]; 419 | _hideControlTimer = nil; 420 | 421 | //创建定时器 422 | _hideControlTimer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:@selector(timerToJudgeHide) userInfo:nil repeats:NO]; 423 | } 424 | //定时隐藏掉控制界面 425 | - (void)timerToJudgeHide{ 426 | 427 | if (_playButton.selected == YES) {//暂停中,不隐藏 428 | return; 429 | } 430 | if (!_isShowControl) {//已经是隐藏状态了 431 | return; 432 | } 433 | 434 | //隐藏 435 | _isShowControl = NO; 436 | [self showOrHideControlView]; 437 | } 438 | 439 | - (void)showOrHideControlView{ 440 | 441 | CGFloat alpha = 0; 442 | if (_isShowControl) { 443 | alpha = 1; 444 | } 445 | 446 | if (self.fullScreenButton.selected) {//全屏 447 | self.panGesture.enabled = YES; 448 | }else{ 449 | if (_halfScreenPanGestureEnabled) { 450 | self.panGesture.enabled = _isShowControl; 451 | } 452 | } 453 | 454 | [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 455 | self.topView.alpha = alpha; 456 | self.bottomView.alpha = alpha; 457 | } completion:^(BOOL finished) { 458 | if (_isShowControl) {//如果当前是显示状态,就要去倒计时隐藏了 459 | [self startHideControlTimer]; 460 | } 461 | }]; 462 | } 463 | 464 | //转换时间格式 465 | - (NSString *)timeFormatted:(NSInteger)totalSeconds 466 | { 467 | NSInteger seconds = totalSeconds % 60; 468 | NSInteger minutes = (totalSeconds / 60) % 60; 469 | 470 | return [NSString stringWithFormat:@"%02ld:%02ld",minutes, seconds]; 471 | } 472 | 473 | #pragma mark - UIGestureRecognizer Delegate 474 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ 475 | 476 | if ([touch.view isDescendantOfView:self.fullScreenView]) { 477 | return YES; 478 | } 479 | return NO; 480 | } 481 | 482 | @end 483 | 484 | -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/resource/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo/LYVideoPlayer/resource/back@2x.png -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/resource/full_screen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo/LYVideoPlayer/resource/full_screen@2x.png -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/resource/normal_screen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo/LYVideoPlayer/resource/normal_screen@2x.png -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/resource/video_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo/LYVideoPlayer/resource/video_pause@2x.png -------------------------------------------------------------------------------- /LYPlayerDemo/LYVideoPlayer/resource/video_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/LYPlayerDemo/LYVideoPlayer/resource/video_play@2x.png -------------------------------------------------------------------------------- /LYPlayerDemo/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/11. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #ifndef PrefixHeader_pch 10 | #define PrefixHeader_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | 15 | #import "LYPlayerHeader.h" 16 | 17 | #endif /* PrefixHeader_pch */ 18 | -------------------------------------------------------------------------------- /LYPlayerDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LYPlayerDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/4. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LYVideoPlayVC.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController{ 17 | BOOL _isHalfScreen; 18 | } 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | self.view.backgroundColor = [UIColor whiteColor]; 23 | 24 | } 25 | - (IBAction)toPlayVideo:(id)sender { 26 | LYVideoPlayVC *videoVC = [[LYVideoPlayVC alloc] init]; 27 | [self presentViewController:videoVC animated:YES completion:nil]; 28 | } 29 | 30 | - (void)didReceiveMemoryWarning { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /LYPlayerDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LYPlayerDemo 4 | // 5 | // Created by liyang on 16/11/11. 6 | // Copyright © 2016年 com.liyang.player. 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 | -------------------------------------------------------------------------------- /LYPlayerDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LYPlayerDemoTests/LYPlayerDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYPlayerDemoTests.m 3 | // LYPlayerDemoTests 4 | // 5 | // Created by liyang on 16/11/11. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LYPlayerDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LYPlayerDemoTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LYPlayerDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LYPlayerDemoUITests/LYPlayerDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LYPlayerDemoUITests.m 3 | // LYPlayerDemoUITests 4 | // 5 | // Created by liyang on 16/11/11. 6 | // Copyright © 2016年 com.liyang.player. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LYPlayerDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LYPlayerDemoUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LYVideoPlayer 2 | 基于AVPlayer封装视频播放器(有自定义滑块、进度条、手势快进快退、手势加减音量等功能) 3 | 4 | 5 | ### 实现的效果: 6 | 7 | ![播放器播放效果.gif](https://github.com/Developer-LiYang/LYVideoPlayer/blob/master/播放效果.gif) 8 | 9 | ![自定义滑块2.png](https://github.com/Developer-LiYang/LYVideoPlayer/blob/master/个性化滑块1.png) 10 | 11 | ![自定义滑块3.png](https://github.com/Developer-LiYang/LYVideoPlayer/blob/master/个性化滑块2.png) 12 | 13 | ### 使用方法 14 | 15 | 1.记得在你的pch文件里导入LVVideoPlayer的头文件LYPlayerHeader.h 16 | 17 | 2.播放器的实例化,即如下两步就可现实视频的播放 18 | ``` 19 | self.videoPlayer = [[LYVideoPlayer alloc] init]; 20 | [self.videoPlayer playWithUrl:self.videoUrl showView:self.view]; 21 | ``` 22 | 如需获得返回按钮和全屏播放按钮的回调,则需遵循协议,并设置代理 23 | ``` 24 | self.videoPlayer.delegate = self; 25 | ``` 26 | 27 | 3.滑块的自定义 - 在实例化LYSlider的时候传入设置相应属性的值就可自定义自己想要的效果 28 | ``` 29 | [_videoSlider setThumbImage:normalImage forState:UIControlStateNormal];//可设置滑块默认状态下的图片 30 | [_videoSlider setThumbImage:highlightImage forState:UIControlStateHighlighted];//可设置滑块高亮状态下的图片 31 | _videoSlider.trackHeight = 1.5;//设置轨道的高度 32 | _videoSlider.thumbVisibleSize = 12;//设置滑块(可见的)大小 33 | ``` 34 | -------------------------------------------------------------------------------- /个性化滑块1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/个性化滑块1.png -------------------------------------------------------------------------------- /个性化滑块2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/个性化滑块2.png -------------------------------------------------------------------------------- /播放效果.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-liyang/LYVideoPlayer/4665c501d5be0ba555c2b79d4f6e002b34ec8082/播放效果.gif --------------------------------------------------------------------------------