├── .gitignore ├── HJVideoPlayer.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── HJVideoPlayer ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── ExampleDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── HJVideoListController.h │ ├── HJVideoListController.m │ ├── ViewController.h │ └── ViewController.m ├── Info.plist ├── VideoClass │ ├── HJVideoAssets.xcassets │ │ ├── Contents.json │ │ └── Video_ICON │ │ │ ├── Contents.json │ │ │ ├── video_back.imageset │ │ │ ├── Contents.json │ │ │ ├── video_back@2x.png │ │ │ └── video_back@3x.png │ │ │ ├── video_farword_left.imageset │ │ │ ├── Contents.json │ │ │ ├── video_farword_left@2x.png │ │ │ └── video_farword_left@3x.png │ │ │ ├── video_farword_right.imageset │ │ │ ├── Contents.json │ │ │ ├── video_farword_right@2x.png │ │ │ └── video_farword_right@3x.png │ │ │ ├── video_pause.imageset │ │ │ ├── Contents.json │ │ │ ├── video_pause@2x.png │ │ │ └── video_pause@3x.png │ │ │ └── video_play.imageset │ │ │ ├── Contents.json │ │ │ ├── video_play@2x.png │ │ │ └── video_play@3x.png │ ├── HJVideoPlayerHeader.h │ ├── Models │ │ ├── HJVideoConfigModel.h │ │ ├── HJVideoConfigModel.m │ │ ├── HJVideoPlayTimeRecord.h │ │ └── HJVideoPlayTimeRecord.m │ ├── Others │ │ ├── AppDelegate+HJVideoRotation.h │ │ ├── AppDelegate+HJVideoRotation.m │ │ ├── HJSingletonService.h │ │ ├── HJVideoConst.h │ │ ├── HJVideoPlayManager.h │ │ ├── HJVideoPlayManager.m │ │ ├── HJVideoPlayerUtil.h │ │ ├── HJVideoPlayerUtil.m │ │ ├── HJVideoTimeUtil.h │ │ ├── HJVideoTimeUtil.m │ │ ├── HJVideoUIManager.h │ │ ├── HJVideoUIManager.m │ │ ├── HJViewFactory.h │ │ ├── HJViewFactory.m │ │ ├── UIDevice+HJVideoRotation.h │ │ ├── UIDevice+HJVideoRotation.m │ │ ├── UIImage+getImage.h │ │ └── UIImage+getImage.m │ ├── Resources │ │ └── videoImages.bundle │ │ │ ├── Back@2x.png │ │ │ ├── Back@3x.png │ │ │ ├── video_back@2x.png │ │ │ ├── video_back@3x.png │ │ │ ├── video_bg.jpg │ │ │ ├── video_farword_left.png │ │ │ ├── video_farword_right.png │ │ │ ├── video_pause@2x.png │ │ │ ├── video_pause@3x.png │ │ │ ├── video_play@2x.png │ │ │ ├── video_play@3x.png │ │ │ ├── video_replay@2x.png │ │ │ ├── video_replay@3x.png │ │ │ ├── video_slider_thumb@2x.png │ │ │ ├── video_slider_thumb@3x.png │ │ │ ├── video_toFullScreen@2x.png │ │ │ ├── video_toFullScreen@3x.png │ │ │ ├── video_toFullScreen_white@2x.png │ │ │ ├── video_toFullScreen_white@3x.png │ │ │ ├── video_toHalfScreen_white@2x.png │ │ │ ├── video_toHalfScreen_white@3x.png │ │ │ ├── 最小化-按下@2x.png │ │ │ └── 最小化-按下@3x.png │ └── Views │ │ ├── HJBufferSlider.h │ │ ├── HJBufferSlider.m │ │ ├── HJCircleLoading.h │ │ ├── HJCircleLoading.m │ │ ├── HJFastForwardView.h │ │ ├── HJFastForwardView.m │ │ ├── HJPlayerView.h │ │ ├── HJPlayerView.m │ │ ├── HJVideoBottomView.h │ │ ├── HJVideoBottomView.m │ │ ├── HJVideoMaskView.h │ │ ├── HJVideoMaskView.m │ │ ├── HJVideoPlayerController.h │ │ ├── HJVideoPlayerController.m │ │ ├── HJVideoSettingView.h │ │ ├── HJVideoSettingView.m │ │ ├── HJVideoTopView.h │ │ └── HJVideoTopView.m └── main.m ├── HJVideoPlayerTests ├── HJVideoPlayerTests.m └── Info.plist ├── HJVideoPlayerUITests ├── HJVideoPlayerUITests.m └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /HJVideoPlayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D29B1C64200C404900E37C21 /* HJVideoPlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D29B1C63200C404900E37C21 /* HJVideoPlayerTests.m */; }; 11 | D29B1C6F200C404900E37C21 /* HJVideoPlayerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = D29B1C6E200C404900E37C21 /* HJVideoPlayerUITests.m */; }; 12 | D29B1CAF200C41BE00E37C21 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D29B1C84200C41BE00E37C21 /* Assets.xcassets */; }; 13 | D29B1CB1200C41BE00E37C21 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D29B1C86200C41BE00E37C21 /* main.m */; }; 14 | D29B1CB6200C41BE00E37C21 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D29B1C91200C41BE00E37C21 /* Info.plist */; }; 15 | D2B1A9152022E53A00619703 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9112022E53A00619703 /* ViewController.m */; }; 16 | D2B1A9162022E53A00619703 /* HJVideoListController.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9122022E53A00619703 /* HJVideoListController.m */; }; 17 | D2B1A9172022E53A00619703 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9132022E53A00619703 /* AppDelegate.m */; }; 18 | D2B1A9B520243D7E00619703 /* videoImages.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D2B1A98820243D7E00619703 /* videoImages.bundle */; }; 19 | D2B1A9B620243D7E00619703 /* HJVideoAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D2B1A98920243D7E00619703 /* HJVideoAssets.xcassets */; }; 20 | D2B1A9B720243D7E00619703 /* HJViewFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A98B20243D7E00619703 /* HJViewFactory.m */; }; 21 | D2B1A9B820243D7E00619703 /* HJVideoTimeUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A98C20243D7E00619703 /* HJVideoTimeUtil.m */; }; 22 | D2B1A9B920243D7E00619703 /* UIDevice+HJVideoRotation.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A98E20243D7E00619703 /* UIDevice+HJVideoRotation.m */; }; 23 | D2B1A9BA20243D7E00619703 /* AppDelegate+HJVideoRotation.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99220243D7E00619703 /* AppDelegate+HJVideoRotation.m */; }; 24 | D2B1A9BB20243D7E00619703 /* HJVideoPlayerUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99420243D7E00619703 /* HJVideoPlayerUtil.m */; }; 25 | D2B1A9BC20243D7E00619703 /* HJVideoUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99720243D7E00619703 /* HJVideoUIManager.m */; }; 26 | D2B1A9BD20243D7E00619703 /* HJVideoPlayManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99A20243D7E00619703 /* HJVideoPlayManager.m */; }; 27 | D2B1A9BE20243D7E00619703 /* UIImage+getImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99B20243D7E00619703 /* UIImage+getImage.m */; }; 28 | D2B1A9BF20243D7E00619703 /* HJVideoConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A99E20243D7E00619703 /* HJVideoConfigModel.m */; }; 29 | D2B1A9C020243D7E00619703 /* HJVideoPlayTimeRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9A120243D7E00619703 /* HJVideoPlayTimeRecord.m */; }; 30 | D2B1A9C120243D7E00619703 /* HJVideoMaskView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9A320243D7E00619703 /* HJVideoMaskView.m */; }; 31 | D2B1A9C220243D7E00619703 /* HJFastForwardView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9A520243D7E00619703 /* HJFastForwardView.m */; }; 32 | D2B1A9C320243D7E00619703 /* HJVideoSettingView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9A820243D7E00619703 /* HJVideoSettingView.m */; }; 33 | D2B1A9C420243D7E00619703 /* HJCircleLoading.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9A920243D7E00619703 /* HJCircleLoading.m */; }; 34 | D2B1A9C520243D7E00619703 /* HJVideoPlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9AA20243D7E00619703 /* HJVideoPlayerController.m */; }; 35 | D2B1A9C620243D7E00619703 /* HJPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9AC20243D7E00619703 /* HJPlayerView.m */; }; 36 | D2B1A9C720243D7E00619703 /* HJBufferSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9AE20243D7E00619703 /* HJBufferSlider.m */; }; 37 | D2B1A9C820243D7E00619703 /* HJVideoBottomView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9B020243D7E00619703 /* HJVideoBottomView.m */; }; 38 | D2B1A9C920243D7E00619703 /* HJVideoTopView.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B1A9B220243D7E00619703 /* HJVideoTopView.m */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXContainerItemProxy section */ 42 | D29B1C60200C404900E37C21 /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = D29B1C3F200C404900E37C21 /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = D29B1C46200C404900E37C21; 47 | remoteInfo = HJVideoPlayer; 48 | }; 49 | D29B1C6B200C404900E37C21 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = D29B1C3F200C404900E37C21 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = D29B1C46200C404900E37C21; 54 | remoteInfo = HJVideoPlayer; 55 | }; 56 | /* End PBXContainerItemProxy section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | D29B1C47200C404900E37C21 /* HJVideoPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HJVideoPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | D29B1C5F200C404900E37C21 /* HJVideoPlayerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HJVideoPlayerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | D29B1C63200C404900E37C21 /* HJVideoPlayerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayerTests.m; sourceTree = ""; }; 62 | D29B1C65200C404900E37C21 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | D29B1C6A200C404900E37C21 /* HJVideoPlayerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HJVideoPlayerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | D29B1C6E200C404900E37C21 /* HJVideoPlayerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayerUITests.m; sourceTree = ""; }; 65 | D29B1C70200C404900E37C21 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | D29B1C84200C41BE00E37C21 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 67 | D29B1C86200C41BE00E37C21 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 68 | D29B1C91200C41BE00E37C21 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | D2B1A90F2022E53A00619703 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 70 | D2B1A9102022E53A00619703 /* HJVideoListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoListController.h; sourceTree = ""; }; 71 | D2B1A9112022E53A00619703 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 72 | D2B1A9122022E53A00619703 /* HJVideoListController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoListController.m; sourceTree = ""; }; 73 | D2B1A9132022E53A00619703 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 74 | D2B1A9142022E53A00619703 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 75 | D2B1A98620243D7E00619703 /* HJVideoPlayerHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoPlayerHeader.h; sourceTree = ""; }; 76 | D2B1A98820243D7E00619703 /* videoImages.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = videoImages.bundle; sourceTree = ""; }; 77 | D2B1A98920243D7E00619703 /* HJVideoAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HJVideoAssets.xcassets; sourceTree = ""; }; 78 | D2B1A98B20243D7E00619703 /* HJViewFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJViewFactory.m; sourceTree = ""; }; 79 | D2B1A98C20243D7E00619703 /* HJVideoTimeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoTimeUtil.m; sourceTree = ""; }; 80 | D2B1A98D20243D7E00619703 /* HJVideoPlayerUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoPlayerUtil.h; sourceTree = ""; }; 81 | D2B1A98E20243D7E00619703 /* UIDevice+HJVideoRotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+HJVideoRotation.m"; sourceTree = ""; }; 82 | D2B1A98F20243D7E00619703 /* HJVideoUIManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoUIManager.h; sourceTree = ""; }; 83 | D2B1A99020243D7E00619703 /* HJVideoConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoConst.h; sourceTree = ""; }; 84 | D2B1A99120243D7E00619703 /* HJVideoPlayManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoPlayManager.h; sourceTree = ""; }; 85 | D2B1A99220243D7E00619703 /* AppDelegate+HJVideoRotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppDelegate+HJVideoRotation.m"; sourceTree = ""; }; 86 | D2B1A99320243D7E00619703 /* UIImage+getImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+getImage.h"; sourceTree = ""; }; 87 | D2B1A99420243D7E00619703 /* HJVideoPlayerUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayerUtil.m; sourceTree = ""; }; 88 | D2B1A99520243D7E00619703 /* HJVideoTimeUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoTimeUtil.h; sourceTree = ""; }; 89 | D2B1A99620243D7E00619703 /* HJViewFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJViewFactory.h; sourceTree = ""; }; 90 | D2B1A99720243D7E00619703 /* HJVideoUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoUIManager.m; sourceTree = ""; }; 91 | D2B1A99820243D7E00619703 /* UIDevice+HJVideoRotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+HJVideoRotation.h"; sourceTree = ""; }; 92 | D2B1A99920243D7E00619703 /* HJSingletonService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJSingletonService.h; sourceTree = ""; }; 93 | D2B1A99A20243D7E00619703 /* HJVideoPlayManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayManager.m; sourceTree = ""; }; 94 | D2B1A99B20243D7E00619703 /* UIImage+getImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+getImage.m"; sourceTree = ""; }; 95 | D2B1A99C20243D7E00619703 /* AppDelegate+HJVideoRotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AppDelegate+HJVideoRotation.h"; sourceTree = ""; }; 96 | D2B1A99E20243D7E00619703 /* HJVideoConfigModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoConfigModel.m; sourceTree = ""; }; 97 | D2B1A99F20243D7E00619703 /* HJVideoPlayTimeRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoPlayTimeRecord.h; sourceTree = ""; }; 98 | D2B1A9A020243D7E00619703 /* HJVideoConfigModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoConfigModel.h; sourceTree = ""; }; 99 | D2B1A9A120243D7E00619703 /* HJVideoPlayTimeRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayTimeRecord.m; sourceTree = ""; }; 100 | D2B1A9A320243D7E00619703 /* HJVideoMaskView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoMaskView.m; sourceTree = ""; }; 101 | D2B1A9A420243D7E00619703 /* HJBufferSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJBufferSlider.h; sourceTree = ""; }; 102 | D2B1A9A520243D7E00619703 /* HJFastForwardView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJFastForwardView.m; sourceTree = ""; }; 103 | D2B1A9A620243D7E00619703 /* HJPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJPlayerView.h; sourceTree = ""; }; 104 | D2B1A9A720243D7E00619703 /* HJVideoBottomView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoBottomView.h; sourceTree = ""; }; 105 | D2B1A9A820243D7E00619703 /* HJVideoSettingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoSettingView.m; sourceTree = ""; }; 106 | D2B1A9A920243D7E00619703 /* HJCircleLoading.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJCircleLoading.m; sourceTree = ""; }; 107 | D2B1A9AA20243D7E00619703 /* HJVideoPlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoPlayerController.m; sourceTree = ""; }; 108 | D2B1A9AB20243D7E00619703 /* HJVideoTopView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoTopView.h; sourceTree = ""; }; 109 | D2B1A9AC20243D7E00619703 /* HJPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJPlayerView.m; sourceTree = ""; }; 110 | D2B1A9AD20243D7E00619703 /* HJFastForwardView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJFastForwardView.h; sourceTree = ""; }; 111 | D2B1A9AE20243D7E00619703 /* HJBufferSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJBufferSlider.m; sourceTree = ""; }; 112 | D2B1A9AF20243D7E00619703 /* HJVideoMaskView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoMaskView.h; sourceTree = ""; }; 113 | D2B1A9B020243D7E00619703 /* HJVideoBottomView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoBottomView.m; sourceTree = ""; }; 114 | D2B1A9B120243D7E00619703 /* HJVideoSettingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoSettingView.h; sourceTree = ""; }; 115 | D2B1A9B220243D7E00619703 /* HJVideoTopView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HJVideoTopView.m; sourceTree = ""; }; 116 | D2B1A9B320243D7E00619703 /* HJCircleLoading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJCircleLoading.h; sourceTree = ""; }; 117 | D2B1A9B420243D7E00619703 /* HJVideoPlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HJVideoPlayerController.h; sourceTree = ""; }; 118 | /* End PBXFileReference section */ 119 | 120 | /* Begin PBXFrameworksBuildPhase section */ 121 | D29B1C44200C404900E37C21 /* Frameworks */ = { 122 | isa = PBXFrameworksBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | D29B1C5C200C404900E37C21 /* Frameworks */ = { 129 | isa = PBXFrameworksBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | D29B1C67200C404900E37C21 /* Frameworks */ = { 136 | isa = PBXFrameworksBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXFrameworksBuildPhase section */ 143 | 144 | /* Begin PBXGroup section */ 145 | D29B1C3E200C404900E37C21 = { 146 | isa = PBXGroup; 147 | children = ( 148 | D29B1C7C200C41BD00E37C21 /* HJVideoPlayer */, 149 | D29B1C62200C404900E37C21 /* HJVideoPlayerTests */, 150 | D29B1C6D200C404900E37C21 /* HJVideoPlayerUITests */, 151 | D29B1C48200C404900E37C21 /* Products */, 152 | ); 153 | sourceTree = ""; 154 | }; 155 | D29B1C48200C404900E37C21 /* Products */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | D29B1C47200C404900E37C21 /* HJVideoPlayer.app */, 159 | D29B1C5F200C404900E37C21 /* HJVideoPlayerTests.xctest */, 160 | D29B1C6A200C404900E37C21 /* HJVideoPlayerUITests.xctest */, 161 | ); 162 | name = Products; 163 | sourceTree = ""; 164 | }; 165 | D29B1C62200C404900E37C21 /* HJVideoPlayerTests */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | D29B1C63200C404900E37C21 /* HJVideoPlayerTests.m */, 169 | D29B1C65200C404900E37C21 /* Info.plist */, 170 | ); 171 | path = HJVideoPlayerTests; 172 | sourceTree = ""; 173 | }; 174 | D29B1C6D200C404900E37C21 /* HJVideoPlayerUITests */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | D29B1C6E200C404900E37C21 /* HJVideoPlayerUITests.m */, 178 | D29B1C70200C404900E37C21 /* Info.plist */, 179 | ); 180 | path = HJVideoPlayerUITests; 181 | sourceTree = ""; 182 | }; 183 | D29B1C7C200C41BD00E37C21 /* HJVideoPlayer */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | D2B1A98520243D7E00619703 /* VideoClass */, 187 | D2B1A90E2022E53A00619703 /* ExampleDemo */, 188 | D29B1C84200C41BE00E37C21 /* Assets.xcassets */, 189 | D29B1C91200C41BE00E37C21 /* Info.plist */, 190 | D29B1C86200C41BE00E37C21 /* main.m */, 191 | ); 192 | path = HJVideoPlayer; 193 | sourceTree = ""; 194 | }; 195 | D2B1A90E2022E53A00619703 /* ExampleDemo */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | D2B1A9142022E53A00619703 /* ViewController.h */, 199 | D2B1A9112022E53A00619703 /* ViewController.m */, 200 | D2B1A9102022E53A00619703 /* HJVideoListController.h */, 201 | D2B1A9122022E53A00619703 /* HJVideoListController.m */, 202 | D2B1A90F2022E53A00619703 /* AppDelegate.h */, 203 | D2B1A9132022E53A00619703 /* AppDelegate.m */, 204 | ); 205 | path = ExampleDemo; 206 | sourceTree = ""; 207 | }; 208 | D2B1A98520243D7E00619703 /* VideoClass */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | D2B1A98620243D7E00619703 /* HJVideoPlayerHeader.h */, 212 | D2B1A98720243D7E00619703 /* Resources */, 213 | D2B1A98920243D7E00619703 /* HJVideoAssets.xcassets */, 214 | D2B1A98A20243D7E00619703 /* Others */, 215 | D2B1A99D20243D7E00619703 /* Models */, 216 | D2B1A9A220243D7E00619703 /* Views */, 217 | ); 218 | path = VideoClass; 219 | sourceTree = ""; 220 | }; 221 | D2B1A98720243D7E00619703 /* Resources */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | D2B1A98820243D7E00619703 /* videoImages.bundle */, 225 | ); 226 | path = Resources; 227 | sourceTree = ""; 228 | }; 229 | D2B1A98A20243D7E00619703 /* Others */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | D2B1A98B20243D7E00619703 /* HJViewFactory.m */, 233 | D2B1A98C20243D7E00619703 /* HJVideoTimeUtil.m */, 234 | D2B1A98D20243D7E00619703 /* HJVideoPlayerUtil.h */, 235 | D2B1A98E20243D7E00619703 /* UIDevice+HJVideoRotation.m */, 236 | D2B1A98F20243D7E00619703 /* HJVideoUIManager.h */, 237 | D2B1A99020243D7E00619703 /* HJVideoConst.h */, 238 | D2B1A99120243D7E00619703 /* HJVideoPlayManager.h */, 239 | D2B1A99220243D7E00619703 /* AppDelegate+HJVideoRotation.m */, 240 | D2B1A99320243D7E00619703 /* UIImage+getImage.h */, 241 | D2B1A99420243D7E00619703 /* HJVideoPlayerUtil.m */, 242 | D2B1A99520243D7E00619703 /* HJVideoTimeUtil.h */, 243 | D2B1A99620243D7E00619703 /* HJViewFactory.h */, 244 | D2B1A99720243D7E00619703 /* HJVideoUIManager.m */, 245 | D2B1A99820243D7E00619703 /* UIDevice+HJVideoRotation.h */, 246 | D2B1A99920243D7E00619703 /* HJSingletonService.h */, 247 | D2B1A99A20243D7E00619703 /* HJVideoPlayManager.m */, 248 | D2B1A99B20243D7E00619703 /* UIImage+getImage.m */, 249 | D2B1A99C20243D7E00619703 /* AppDelegate+HJVideoRotation.h */, 250 | ); 251 | path = Others; 252 | sourceTree = ""; 253 | }; 254 | D2B1A99D20243D7E00619703 /* Models */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | D2B1A99E20243D7E00619703 /* HJVideoConfigModel.m */, 258 | D2B1A99F20243D7E00619703 /* HJVideoPlayTimeRecord.h */, 259 | D2B1A9A020243D7E00619703 /* HJVideoConfigModel.h */, 260 | D2B1A9A120243D7E00619703 /* HJVideoPlayTimeRecord.m */, 261 | ); 262 | path = Models; 263 | sourceTree = ""; 264 | }; 265 | D2B1A9A220243D7E00619703 /* Views */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | D2B1A9A320243D7E00619703 /* HJVideoMaskView.m */, 269 | D2B1A9A420243D7E00619703 /* HJBufferSlider.h */, 270 | D2B1A9A520243D7E00619703 /* HJFastForwardView.m */, 271 | D2B1A9A620243D7E00619703 /* HJPlayerView.h */, 272 | D2B1A9A720243D7E00619703 /* HJVideoBottomView.h */, 273 | D2B1A9A820243D7E00619703 /* HJVideoSettingView.m */, 274 | D2B1A9A920243D7E00619703 /* HJCircleLoading.m */, 275 | D2B1A9AA20243D7E00619703 /* HJVideoPlayerController.m */, 276 | D2B1A9AB20243D7E00619703 /* HJVideoTopView.h */, 277 | D2B1A9AC20243D7E00619703 /* HJPlayerView.m */, 278 | D2B1A9AD20243D7E00619703 /* HJFastForwardView.h */, 279 | D2B1A9AE20243D7E00619703 /* HJBufferSlider.m */, 280 | D2B1A9AF20243D7E00619703 /* HJVideoMaskView.h */, 281 | D2B1A9B020243D7E00619703 /* HJVideoBottomView.m */, 282 | D2B1A9B120243D7E00619703 /* HJVideoSettingView.h */, 283 | D2B1A9B220243D7E00619703 /* HJVideoTopView.m */, 284 | D2B1A9B320243D7E00619703 /* HJCircleLoading.h */, 285 | D2B1A9B420243D7E00619703 /* HJVideoPlayerController.h */, 286 | ); 287 | path = Views; 288 | sourceTree = ""; 289 | }; 290 | /* End PBXGroup section */ 291 | 292 | /* Begin PBXNativeTarget section */ 293 | D29B1C46200C404900E37C21 /* HJVideoPlayer */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = D29B1C73200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayer" */; 296 | buildPhases = ( 297 | D29B1C43200C404900E37C21 /* Sources */, 298 | D29B1C44200C404900E37C21 /* Frameworks */, 299 | D29B1C45200C404900E37C21 /* Resources */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | ); 305 | name = HJVideoPlayer; 306 | productName = HJVideoPlayer; 307 | productReference = D29B1C47200C404900E37C21 /* HJVideoPlayer.app */; 308 | productType = "com.apple.product-type.application"; 309 | }; 310 | D29B1C5E200C404900E37C21 /* HJVideoPlayerTests */ = { 311 | isa = PBXNativeTarget; 312 | buildConfigurationList = D29B1C76200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayerTests" */; 313 | buildPhases = ( 314 | D29B1C5B200C404900E37C21 /* Sources */, 315 | D29B1C5C200C404900E37C21 /* Frameworks */, 316 | D29B1C5D200C404900E37C21 /* Resources */, 317 | ); 318 | buildRules = ( 319 | ); 320 | dependencies = ( 321 | D29B1C61200C404900E37C21 /* PBXTargetDependency */, 322 | ); 323 | name = HJVideoPlayerTests; 324 | productName = HJVideoPlayerTests; 325 | productReference = D29B1C5F200C404900E37C21 /* HJVideoPlayerTests.xctest */; 326 | productType = "com.apple.product-type.bundle.unit-test"; 327 | }; 328 | D29B1C69200C404900E37C21 /* HJVideoPlayerUITests */ = { 329 | isa = PBXNativeTarget; 330 | buildConfigurationList = D29B1C79200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayerUITests" */; 331 | buildPhases = ( 332 | D29B1C66200C404900E37C21 /* Sources */, 333 | D29B1C67200C404900E37C21 /* Frameworks */, 334 | D29B1C68200C404900E37C21 /* Resources */, 335 | ); 336 | buildRules = ( 337 | ); 338 | dependencies = ( 339 | D29B1C6C200C404900E37C21 /* PBXTargetDependency */, 340 | ); 341 | name = HJVideoPlayerUITests; 342 | productName = HJVideoPlayerUITests; 343 | productReference = D29B1C6A200C404900E37C21 /* HJVideoPlayerUITests.xctest */; 344 | productType = "com.apple.product-type.bundle.ui-testing"; 345 | }; 346 | /* End PBXNativeTarget section */ 347 | 348 | /* Begin PBXProject section */ 349 | D29B1C3F200C404900E37C21 /* Project object */ = { 350 | isa = PBXProject; 351 | attributes = { 352 | LastUpgradeCheck = 0920; 353 | ORGANIZATIONNAME = WHJ; 354 | TargetAttributes = { 355 | D29B1C46200C404900E37C21 = { 356 | CreatedOnToolsVersion = 9.2; 357 | ProvisioningStyle = Automatic; 358 | }; 359 | D29B1C5E200C404900E37C21 = { 360 | CreatedOnToolsVersion = 9.2; 361 | ProvisioningStyle = Automatic; 362 | TestTargetID = D29B1C46200C404900E37C21; 363 | }; 364 | D29B1C69200C404900E37C21 = { 365 | CreatedOnToolsVersion = 9.2; 366 | ProvisioningStyle = Automatic; 367 | TestTargetID = D29B1C46200C404900E37C21; 368 | }; 369 | }; 370 | }; 371 | buildConfigurationList = D29B1C42200C404900E37C21 /* Build configuration list for PBXProject "HJVideoPlayer" */; 372 | compatibilityVersion = "Xcode 8.0"; 373 | developmentRegion = en; 374 | hasScannedForEncodings = 0; 375 | knownRegions = ( 376 | en, 377 | Base, 378 | ); 379 | mainGroup = D29B1C3E200C404900E37C21; 380 | productRefGroup = D29B1C48200C404900E37C21 /* Products */; 381 | projectDirPath = ""; 382 | projectRoot = ""; 383 | targets = ( 384 | D29B1C46200C404900E37C21 /* HJVideoPlayer */, 385 | D29B1C5E200C404900E37C21 /* HJVideoPlayerTests */, 386 | D29B1C69200C404900E37C21 /* HJVideoPlayerUITests */, 387 | ); 388 | }; 389 | /* End PBXProject section */ 390 | 391 | /* Begin PBXResourcesBuildPhase section */ 392 | D29B1C45200C404900E37C21 /* Resources */ = { 393 | isa = PBXResourcesBuildPhase; 394 | buildActionMask = 2147483647; 395 | files = ( 396 | D29B1CAF200C41BE00E37C21 /* Assets.xcassets in Resources */, 397 | D2B1A9B520243D7E00619703 /* videoImages.bundle in Resources */, 398 | D2B1A9B620243D7E00619703 /* HJVideoAssets.xcassets in Resources */, 399 | D29B1CB6200C41BE00E37C21 /* Info.plist in Resources */, 400 | ); 401 | runOnlyForDeploymentPostprocessing = 0; 402 | }; 403 | D29B1C5D200C404900E37C21 /* Resources */ = { 404 | isa = PBXResourcesBuildPhase; 405 | buildActionMask = 2147483647; 406 | files = ( 407 | ); 408 | runOnlyForDeploymentPostprocessing = 0; 409 | }; 410 | D29B1C68200C404900E37C21 /* Resources */ = { 411 | isa = PBXResourcesBuildPhase; 412 | buildActionMask = 2147483647; 413 | files = ( 414 | ); 415 | runOnlyForDeploymentPostprocessing = 0; 416 | }; 417 | /* End PBXResourcesBuildPhase section */ 418 | 419 | /* Begin PBXSourcesBuildPhase section */ 420 | D29B1C43200C404900E37C21 /* Sources */ = { 421 | isa = PBXSourcesBuildPhase; 422 | buildActionMask = 2147483647; 423 | files = ( 424 | D2B1A9BE20243D7E00619703 /* UIImage+getImage.m in Sources */, 425 | D2B1A9C320243D7E00619703 /* HJVideoSettingView.m in Sources */, 426 | D2B1A9BA20243D7E00619703 /* AppDelegate+HJVideoRotation.m in Sources */, 427 | D2B1A9B820243D7E00619703 /* HJVideoTimeUtil.m in Sources */, 428 | D2B1A9C720243D7E00619703 /* HJBufferSlider.m in Sources */, 429 | D2B1A9C920243D7E00619703 /* HJVideoTopView.m in Sources */, 430 | D2B1A9C420243D7E00619703 /* HJCircleLoading.m in Sources */, 431 | D2B1A9C120243D7E00619703 /* HJVideoMaskView.m in Sources */, 432 | D2B1A9C820243D7E00619703 /* HJVideoBottomView.m in Sources */, 433 | D2B1A9BB20243D7E00619703 /* HJVideoPlayerUtil.m in Sources */, 434 | D2B1A9C620243D7E00619703 /* HJPlayerView.m in Sources */, 435 | D2B1A9172022E53A00619703 /* AppDelegate.m in Sources */, 436 | D29B1CB1200C41BE00E37C21 /* main.m in Sources */, 437 | D2B1A9C520243D7E00619703 /* HJVideoPlayerController.m in Sources */, 438 | D2B1A9BD20243D7E00619703 /* HJVideoPlayManager.m in Sources */, 439 | D2B1A9BF20243D7E00619703 /* HJVideoConfigModel.m in Sources */, 440 | D2B1A9162022E53A00619703 /* HJVideoListController.m in Sources */, 441 | D2B1A9B920243D7E00619703 /* UIDevice+HJVideoRotation.m in Sources */, 442 | D2B1A9B720243D7E00619703 /* HJViewFactory.m in Sources */, 443 | D2B1A9152022E53A00619703 /* ViewController.m in Sources */, 444 | D2B1A9BC20243D7E00619703 /* HJVideoUIManager.m in Sources */, 445 | D2B1A9C220243D7E00619703 /* HJFastForwardView.m in Sources */, 446 | D2B1A9C020243D7E00619703 /* HJVideoPlayTimeRecord.m in Sources */, 447 | ); 448 | runOnlyForDeploymentPostprocessing = 0; 449 | }; 450 | D29B1C5B200C404900E37C21 /* Sources */ = { 451 | isa = PBXSourcesBuildPhase; 452 | buildActionMask = 2147483647; 453 | files = ( 454 | D29B1C64200C404900E37C21 /* HJVideoPlayerTests.m in Sources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | D29B1C66200C404900E37C21 /* Sources */ = { 459 | isa = PBXSourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | D29B1C6F200C404900E37C21 /* HJVideoPlayerUITests.m in Sources */, 463 | ); 464 | runOnlyForDeploymentPostprocessing = 0; 465 | }; 466 | /* End PBXSourcesBuildPhase section */ 467 | 468 | /* Begin PBXTargetDependency section */ 469 | D29B1C61200C404900E37C21 /* PBXTargetDependency */ = { 470 | isa = PBXTargetDependency; 471 | target = D29B1C46200C404900E37C21 /* HJVideoPlayer */; 472 | targetProxy = D29B1C60200C404900E37C21 /* PBXContainerItemProxy */; 473 | }; 474 | D29B1C6C200C404900E37C21 /* PBXTargetDependency */ = { 475 | isa = PBXTargetDependency; 476 | target = D29B1C46200C404900E37C21 /* HJVideoPlayer */; 477 | targetProxy = D29B1C6B200C404900E37C21 /* PBXContainerItemProxy */; 478 | }; 479 | /* End PBXTargetDependency section */ 480 | 481 | /* Begin XCBuildConfiguration section */ 482 | D29B1C71200C404900E37C21 /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ALWAYS_SEARCH_USER_PATHS = NO; 486 | CLANG_ANALYZER_NONNULL = YES; 487 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 488 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 489 | CLANG_CXX_LIBRARY = "libc++"; 490 | CLANG_ENABLE_MODULES = YES; 491 | CLANG_ENABLE_OBJC_ARC = YES; 492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_COMMA = YES; 495 | CLANG_WARN_CONSTANT_CONVERSION = YES; 496 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 497 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 498 | CLANG_WARN_EMPTY_BODY = YES; 499 | CLANG_WARN_ENUM_CONVERSION = YES; 500 | CLANG_WARN_INFINITE_RECURSION = YES; 501 | CLANG_WARN_INT_CONVERSION = YES; 502 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 503 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 504 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 505 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 506 | CLANG_WARN_STRICT_PROTOTYPES = YES; 507 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 508 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 509 | CLANG_WARN_UNREACHABLE_CODE = YES; 510 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 511 | CODE_SIGN_IDENTITY = "iPhone Developer"; 512 | COPY_PHASE_STRIP = NO; 513 | DEBUG_INFORMATION_FORMAT = dwarf; 514 | ENABLE_STRICT_OBJC_MSGSEND = YES; 515 | ENABLE_TESTABILITY = YES; 516 | GCC_C_LANGUAGE_STANDARD = gnu11; 517 | GCC_DYNAMIC_NO_PIC = NO; 518 | GCC_NO_COMMON_BLOCKS = YES; 519 | GCC_OPTIMIZATION_LEVEL = 0; 520 | GCC_PREPROCESSOR_DEFINITIONS = ( 521 | "DEBUG=1", 522 | "$(inherited)", 523 | ); 524 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 525 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 526 | GCC_WARN_UNDECLARED_SELECTOR = YES; 527 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 528 | GCC_WARN_UNUSED_FUNCTION = YES; 529 | GCC_WARN_UNUSED_VARIABLE = YES; 530 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 531 | MTL_ENABLE_DEBUG_INFO = YES; 532 | ONLY_ACTIVE_ARCH = YES; 533 | SDKROOT = iphoneos; 534 | }; 535 | name = Debug; 536 | }; 537 | D29B1C72200C404900E37C21 /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | ALWAYS_SEARCH_USER_PATHS = NO; 541 | CLANG_ANALYZER_NONNULL = YES; 542 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 543 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 544 | CLANG_CXX_LIBRARY = "libc++"; 545 | CLANG_ENABLE_MODULES = YES; 546 | CLANG_ENABLE_OBJC_ARC = YES; 547 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 548 | CLANG_WARN_BOOL_CONVERSION = YES; 549 | CLANG_WARN_COMMA = YES; 550 | CLANG_WARN_CONSTANT_CONVERSION = YES; 551 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 552 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 553 | CLANG_WARN_EMPTY_BODY = YES; 554 | CLANG_WARN_ENUM_CONVERSION = YES; 555 | CLANG_WARN_INFINITE_RECURSION = YES; 556 | CLANG_WARN_INT_CONVERSION = YES; 557 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 558 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 559 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 560 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 561 | CLANG_WARN_STRICT_PROTOTYPES = YES; 562 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 563 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 564 | CLANG_WARN_UNREACHABLE_CODE = YES; 565 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 566 | CODE_SIGN_IDENTITY = "iPhone Developer"; 567 | COPY_PHASE_STRIP = NO; 568 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 569 | ENABLE_NS_ASSERTIONS = NO; 570 | ENABLE_STRICT_OBJC_MSGSEND = YES; 571 | GCC_C_LANGUAGE_STANDARD = gnu11; 572 | GCC_NO_COMMON_BLOCKS = YES; 573 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 574 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 575 | GCC_WARN_UNDECLARED_SELECTOR = YES; 576 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 577 | GCC_WARN_UNUSED_FUNCTION = YES; 578 | GCC_WARN_UNUSED_VARIABLE = YES; 579 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 580 | MTL_ENABLE_DEBUG_INFO = NO; 581 | SDKROOT = iphoneos; 582 | VALIDATE_PRODUCT = YES; 583 | }; 584 | name = Release; 585 | }; 586 | D29B1C74200C404900E37C21 /* Debug */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 590 | CODE_SIGN_STYLE = Automatic; 591 | DEVELOPMENT_TEAM = 7BY6XUZ4WR; 592 | INFOPLIST_FILE = HJVideoPlayer/Info.plist; 593 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 594 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 595 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayer; 596 | PRODUCT_NAME = "$(TARGET_NAME)"; 597 | TARGETED_DEVICE_FAMILY = 1; 598 | }; 599 | name = Debug; 600 | }; 601 | D29B1C75200C404900E37C21 /* Release */ = { 602 | isa = XCBuildConfiguration; 603 | buildSettings = { 604 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 605 | CODE_SIGN_STYLE = Automatic; 606 | DEVELOPMENT_TEAM = 7BY6XUZ4WR; 607 | INFOPLIST_FILE = HJVideoPlayer/Info.plist; 608 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 609 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 610 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayer; 611 | PRODUCT_NAME = "$(TARGET_NAME)"; 612 | TARGETED_DEVICE_FAMILY = 1; 613 | }; 614 | name = Release; 615 | }; 616 | D29B1C77200C404900E37C21 /* Debug */ = { 617 | isa = XCBuildConfiguration; 618 | buildSettings = { 619 | BUNDLE_LOADER = "$(TEST_HOST)"; 620 | CODE_SIGN_STYLE = Automatic; 621 | DEVELOPMENT_TEAM = 2PZMTS6D3B; 622 | INFOPLIST_FILE = HJVideoPlayerTests/Info.plist; 623 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 624 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayerTests; 625 | PRODUCT_NAME = "$(TARGET_NAME)"; 626 | TARGETED_DEVICE_FAMILY = "1,2"; 627 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HJVideoPlayer.app/HJVideoPlayer"; 628 | }; 629 | name = Debug; 630 | }; 631 | D29B1C78200C404900E37C21 /* Release */ = { 632 | isa = XCBuildConfiguration; 633 | buildSettings = { 634 | BUNDLE_LOADER = "$(TEST_HOST)"; 635 | CODE_SIGN_STYLE = Automatic; 636 | DEVELOPMENT_TEAM = 2PZMTS6D3B; 637 | INFOPLIST_FILE = HJVideoPlayerTests/Info.plist; 638 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 639 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayerTests; 640 | PRODUCT_NAME = "$(TARGET_NAME)"; 641 | TARGETED_DEVICE_FAMILY = "1,2"; 642 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HJVideoPlayer.app/HJVideoPlayer"; 643 | }; 644 | name = Release; 645 | }; 646 | D29B1C7A200C404900E37C21 /* Debug */ = { 647 | isa = XCBuildConfiguration; 648 | buildSettings = { 649 | CODE_SIGN_STYLE = Automatic; 650 | DEVELOPMENT_TEAM = 2PZMTS6D3B; 651 | INFOPLIST_FILE = HJVideoPlayerUITests/Info.plist; 652 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 653 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayerUITests; 654 | PRODUCT_NAME = "$(TARGET_NAME)"; 655 | TARGETED_DEVICE_FAMILY = "1,2"; 656 | TEST_TARGET_NAME = HJVideoPlayer; 657 | }; 658 | name = Debug; 659 | }; 660 | D29B1C7B200C404900E37C21 /* Release */ = { 661 | isa = XCBuildConfiguration; 662 | buildSettings = { 663 | CODE_SIGN_STYLE = Automatic; 664 | DEVELOPMENT_TEAM = 2PZMTS6D3B; 665 | INFOPLIST_FILE = HJVideoPlayerUITests/Info.plist; 666 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 667 | PRODUCT_BUNDLE_IDENTIFIER = com.psl.whj.HJVideoPlayerUITests; 668 | PRODUCT_NAME = "$(TARGET_NAME)"; 669 | TARGETED_DEVICE_FAMILY = "1,2"; 670 | TEST_TARGET_NAME = HJVideoPlayer; 671 | }; 672 | name = Release; 673 | }; 674 | /* End XCBuildConfiguration section */ 675 | 676 | /* Begin XCConfigurationList section */ 677 | D29B1C42200C404900E37C21 /* Build configuration list for PBXProject "HJVideoPlayer" */ = { 678 | isa = XCConfigurationList; 679 | buildConfigurations = ( 680 | D29B1C71200C404900E37C21 /* Debug */, 681 | D29B1C72200C404900E37C21 /* Release */, 682 | ); 683 | defaultConfigurationIsVisible = 0; 684 | defaultConfigurationName = Release; 685 | }; 686 | D29B1C73200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayer" */ = { 687 | isa = XCConfigurationList; 688 | buildConfigurations = ( 689 | D29B1C74200C404900E37C21 /* Debug */, 690 | D29B1C75200C404900E37C21 /* Release */, 691 | ); 692 | defaultConfigurationIsVisible = 0; 693 | defaultConfigurationName = Release; 694 | }; 695 | D29B1C76200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayerTests" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | D29B1C77200C404900E37C21 /* Debug */, 699 | D29B1C78200C404900E37C21 /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | D29B1C79200C404900E37C21 /* Build configuration list for PBXNativeTarget "HJVideoPlayerUITests" */ = { 705 | isa = XCConfigurationList; 706 | buildConfigurations = ( 707 | D29B1C7A200C404900E37C21 /* Debug */, 708 | D29B1C7B200C404900E37C21 /* Release */, 709 | ); 710 | defaultConfigurationIsVisible = 0; 711 | defaultConfigurationName = Release; 712 | }; 713 | /* End XCConfigurationList section */ 714 | }; 715 | rootObject = D29B1C3F200C404900E37C21 /* Project object */; 716 | } 717 | -------------------------------------------------------------------------------- /HJVideoPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HJVideoPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /HJVideoPlayer/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "minimum-system-version" : "7.0", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "orientation" : "portrait", 11 | "idiom" : "iphone", 12 | "minimum-system-version" : "7.0", 13 | "subtype" : "retina4", 14 | "scale" : "2x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. 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 | -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | ViewController *tmpVC = [[ViewController alloc]init]; 26 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:tmpVC]; 27 | self.window.rootViewController = nav; 28 | 29 | return YES; 30 | } 31 | 32 | 33 | - (void)applicationWillResignActive:(UIApplication *)application { 34 | // 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. 35 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 36 | } 37 | 38 | 39 | - (void)applicationDidEnterBackground:(UIApplication *)application { 40 | // 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. 41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 42 | } 43 | 44 | 45 | - (void)applicationWillEnterForeground:(UIApplication *)application { 46 | // 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. 47 | } 48 | 49 | 50 | - (void)applicationDidBecomeActive:(UIApplication *)application { 51 | // 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. 52 | } 53 | 54 | 55 | - (void)applicationWillTerminate:(UIApplication *)application { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/HJVideoListController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoListController.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoListController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/HJVideoListController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoListController.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoListController.h" 10 | #import "HJVideoPlayerController.h" 11 | #import "HJVideoPlayerHeader.h" 12 | #import "HJVideoConst.h" 13 | 14 | @interface HJVideoListController () 15 | 16 | /** 视频播放器 */ 17 | @property (nonatomic, strong) HJVideoPlayerController *videoPlayerVC; 18 | 19 | /** 数据 */ 20 | @property (nonatomic, strong) NSArray *urls; 21 | 22 | @property (nonatomic, strong) NSArray *names; 23 | 24 | 25 | @end 26 | 27 | @implementation HJVideoListController 28 | 29 | #pragma mark - Life Circle 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | 33 | [self buildDatas]; 34 | 35 | [self setupUI]; 36 | } 37 | - (void)didReceiveMemoryWarning { 38 | [super didReceiveMemoryWarning]; 39 | } 40 | 41 | 42 | - (void)viewWillAppear:(BOOL)animated{ 43 | [super viewWillAppear:animated]; 44 | 45 | [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:NO]; 46 | } 47 | 48 | - (void)viewWillDisappear:(BOOL)animated{ 49 | [super viewWillDisappear:animated]; 50 | 51 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO]; 52 | } 53 | - (void)dealloc{ 54 | 55 | NSLog(@"视频播放列表销毁了"); 56 | } 57 | 58 | - (void)buildDatas{ 59 | 60 | //@"https://oss.zonghenggongkao.cn/course/microcourse/shenlun/sg02.mp4 61 | self.urls = @[@"http://hc34.aipai.com/user/128/31977128/6009407/card/44044719/card.mp4?l=f", 62 | @"http://hc31.aipai.com/user/128/31977128/1006/card/44340096/card.mp4?l=f" 63 | ]; 64 | 65 | self.names = @[@"王者荣耀德古拉:44杀貂蝉?谁能阻挡我", 66 | @"德古拉貂蝉"]; 67 | 68 | } 69 | #pragma mark - About UI 70 | - (void)setupUI{ 71 | 72 | kVideoPlayerManager.maxRecordCount = 2; 73 | HJVideoPlayerController *videoPlayer = [[HJVideoPlayerController alloc]initWithFrame:CGRectMake(0, 0, kVideoScreenW, VideoH(kVideoScreenW))]; 74 | [self.view addSubview:videoPlayer.view]; 75 | [self addChildViewController:videoPlayer]; 76 | self.videoPlayerVC = videoPlayer; 77 | [self.videoPlayerVC setUrl:self.urls[0]]; 78 | [self.videoPlayerVC setTitle:self.names[0]]; 79 | 80 | UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(videoPlayer.view.frame), kVideoScreenW, kVideoScreenH-CGRectGetHeight(videoPlayer.view.frame)) style:UITableViewStylePlain]; 81 | tableView.delegate = self; 82 | tableView.dataSource = self; 83 | [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"]; 84 | 85 | [self.view addSubview:tableView]; 86 | 87 | [tableView reloadData]; 88 | } 89 | 90 | #pragma mark - Request Data 91 | 92 | #pragma mark - Pravite Method 93 | 94 | #pragma mark - Public Method 95 | 96 | #pragma mark - Event response 97 | 98 | #pragma mark - Delegate methods 99 | 100 | #pragma mark - UITableViewDataSource 101 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 102 | 103 | return self.names.count; 104 | } 105 | 106 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ 107 | 108 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; 109 | cell.textLabel.text = self.names[indexPath.row]; 110 | return cell; 111 | } 112 | 113 | 114 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 115 | 116 | return 1; 117 | } 118 | 119 | #pragma mark - UITableViewDelegate 120 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 121 | 122 | NSLog(@"name:%@ url=%@",self.names[indexPath.row],self.urls[indexPath.row]); 123 | [self.videoPlayerVC.configModel setOnlyFullScreen:NO]; 124 | [self.videoPlayerVC setUrl:self.urls[indexPath.row]]; 125 | [self.videoPlayerVC setVideoTitle:self.names[indexPath.row]]; 126 | } 127 | 128 | 129 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 130 | return 44.f; 131 | } 132 | 133 | 134 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ 135 | 136 | return 0.001f; 137 | } 138 | 139 | 140 | #pragma mark - Getters/Setters/Lazy 141 | @end 142 | -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /HJVideoPlayer/ExampleDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "HJVideoPlayerController.h" 11 | #import "HJVideoListController.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | UIButton * enterBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 23 | [enterBtn setTitle:@"进入视频播放列表" forState:UIControlStateNormal]; 24 | [enterBtn setFrame:CGRectMake(0, 0, 200, 44)]; 25 | [enterBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 26 | [enterBtn setCenter:self.view.center]; 27 | [enterBtn addTarget:self action:@selector(enterAction) forControlEvents:UIControlEventTouchUpInside]; 28 | [self.view addSubview:enterBtn]; 29 | 30 | UIButton * fullBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 31 | [fullBtn setTitle:@"进入全屏播放" forState:UIControlStateNormal]; 32 | CGRect fullBtnFrame = enterBtn.frame; 33 | fullBtnFrame.origin.y = CGRectGetMaxY(enterBtn.frame)+20; 34 | [fullBtn setFrame:fullBtnFrame]; 35 | [fullBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 36 | [fullBtn addTarget:self action:@selector(enterFull) forControlEvents:UIControlEventTouchUpInside]; 37 | [self.view addSubview:fullBtn]; 38 | } 39 | 40 | 41 | 42 | - (void)enterAction{ 43 | 44 | HJVideoListController *listVC = [[HJVideoListController alloc] init]; 45 | [self.navigationController pushViewController:listVC animated:YES]; 46 | } 47 | 48 | 49 | - (void)enterFull{ 50 | 51 | HJVideoPlayerController * videoC = [[HJVideoPlayerController alloc] init]; 52 | [videoC.configModel setOnlyFullScreen:YES]; 53 | [videoC setUrl:@"http://hc31.aipai.com/user/128/31977128/1006/card/44340096/card.mp4?l=f"]; 54 | [self.navigationController pushViewController:videoC animated:YES]; 55 | 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /HJVideoPlayer/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSCameraUsageDescription 29 | 访问相机 30 | NSLocationAlwaysUsageDescription 31 | 始终访问位置 32 | NSLocationUsageDescription 33 | 访问位置 34 | NSPhotoLibraryUsageDescription 35 | 访问相册 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UIStatusBarStyle 43 | UIStatusBarStyleLightContent 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_back.imageset/video_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_back.imageset/video_back@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_back.imageset/video_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_back.imageset/video_back@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_farword_left@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_farword_left@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_left.imageset/video_farword_left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_left.imageset/video_farword_left@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_left.imageset/video_farword_left@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_left.imageset/video_farword_left@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_farword_right@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_farword_right@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_right.imageset/video_farword_right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_right.imageset/video_farword_right@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_right.imageset/video_farword_right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_farword_right.imageset/video_farword_right@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_pause@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_pause@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_pause.imageset/video_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_pause.imageset/video_pause@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_pause.imageset/video_pause@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_pause.imageset/video_pause@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "video_play@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "video_play@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_play.imageset/video_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_play.imageset/video_play@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_play.imageset/video_play@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/HJVideoAssets.xcassets/Video_ICON/video_play.imageset/video_play@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/HJVideoPlayerHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerHeader.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #ifndef HJVideoPlayerHeader_h 10 | #define HJVideoPlayerHeader_h 11 | 12 | 13 | #import "HJVideoPlayerController.h" 14 | 15 | #import "HJVideoPlayManager.h" 16 | 17 | #import "HJVideoConst.h" 18 | 19 | #endif /* HJVideoPlayerHeader_h */ 20 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Models/HJVideoConfigModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoConfigModel.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/30. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoConfigModel : NSObject 12 | 13 | /** 是否仅支持全屏 */ 14 | @property (nonatomic, assign) BOOL onlyFullScreen; 15 | /** 设置URL后自动播放 */ 16 | @property (nonatomic, assign) BOOL autoPlay; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Models/HJVideoConfigModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoConfigModel.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/30. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoConfigModel.h" 10 | 11 | @implementation HJVideoConfigModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Models/HJVideoPlayTimeRecord.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayTimeRecord.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/2/1. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoPlayTimeRecord : NSObject 12 | 13 | /** 播放url */ 14 | @property (nonatomic, copy) NSString *url; 15 | 16 | /** 播放时长记录 */ 17 | @property (nonatomic, assign) float playTime; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Models/HJVideoPlayTimeRecord.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayTimeRecord.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/2/1. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoPlayTimeRecord.h" 10 | 11 | @implementation HJVideoPlayTimeRecord 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/AppDelegate+HJVideoRotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+HJVideoRotation.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | 12 | @interface AppDelegate (HJVideoRotation) 13 | 14 | #pragma mark - 添加屏幕旋转控制 15 | @property (nonatomic, assign) UIInterfaceOrientationMask orientationMask; 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/AppDelegate+HJVideoRotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate+HJVideoRotation.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate+HJVideoRotation.h" 10 | #import 11 | @implementation AppDelegate (HJVideoRotation) 12 | 13 | 14 | 15 | #pragma mark - 添加屏幕旋转控制 16 | - (UIInterfaceOrientationMask)orientationMask { 17 | UIInterfaceOrientationMask mask = [objc_getAssociatedObject(self, @selector(orientationMask)) integerValue]; 18 | if (mask == 0) { 19 | mask = UIInterfaceOrientationMaskPortrait; 20 | } 21 | return mask; 22 | } 23 | 24 | - (void)setOrientationMask:(UIInterfaceOrientationMask)orientationMask{ 25 | objc_setAssociatedObject(self, @selector(orientationMask), @(orientationMask), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 26 | } 27 | 28 | //override 29 | - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 30 | { 31 | UIInterfaceOrientationMask mask = [self orientationMask]; 32 | return mask; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJSingletonService.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJSingletonService.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define ServiceSingletonH(methodName) + (instancetype)shared##methodName; 12 | 13 | #define ServiceSingletonM(methodName) \ 14 | static id instace = nil; \ 15 | + (id)allocWithZone:(struct _NSZone *)zone \ 16 | { \ 17 | if (instace == nil) { \ 18 | static dispatch_once_t onceToken; \ 19 | dispatch_once(&onceToken, ^{ \ 20 | instace = [super allocWithZone:zone]; \ 21 | }); \ 22 | } \ 23 | return instace; \ 24 | } \ 25 | \ 26 | - (id)init \ 27 | { \ 28 | static dispatch_once_t onceToken; \ 29 | dispatch_once(&onceToken, ^{ \ 30 | instace = [super init]; \ 31 | }); \ 32 | return instace; \ 33 | } \ 34 | \ 35 | + (instancetype)shared##methodName \ 36 | { \ 37 | return [[self alloc] init]; \ 38 | } \ 39 | - (id)copyWithZone:(struct _NSZone *)zone \ 40 | { \ 41 | return instace; \ 42 | } \ 43 | \ 44 | - (id)mutableCopyWithZone:(struct _NSZone *)zone{\ 45 | return instace;\ 46 | } 47 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoConst.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoConst.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/26. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #ifndef HJVideoConst_h 10 | #define HJVideoConst_h 11 | 12 | 13 | /********************** CONSTS ***********************/ 14 | 15 | #define kVideoFullFrame [UIScreen mainScreen].bounds 16 | #define kVideoScreenW [UIScreen mainScreen].bounds.size.width 17 | #define kVideoScreenH [UIScreen mainScreen].bounds.size.height 18 | 19 | 20 | #define kVideoMaskViewBGColor [[UIColor blackColor] colorWithAlphaComponent:.3] 21 | 22 | #define kVideoBottomGradientColor [[UIColor blackColor] colorWithAlphaComponent:.5] 23 | 24 | 25 | static CGFloat const kDefaultAnimationDuration = 0.35f; 26 | 27 | static CGFloat const kTopBarHalfHeight = 44.f; 28 | static CGFloat const kTopBarFullHeight = 44.f; 29 | static CGFloat const kBottomBarHalfHeight = 44.f; 30 | static CGFloat const kBottomBarFullHeight = 44.f; 31 | 32 | /********************** Images ***********************/ 33 | 34 | #define imgFullScreen [UIImage imageFromBundleWithName:@"video_toFullScreen_white"] 35 | #define imgHalfScreen [UIImage imageFromBundleWithName:@"video_toHalfScreen_white"] 36 | #define imgSliderThumb [UIImage imageFromBundleWithName:@"video_slider_thumb"] 37 | 38 | #define imgVideoBackImg [UIImage imageFromBundleWithName:@"video_bg.jpg"] 39 | 40 | #define imgPlay [UIImage imageNamed:@"video_play"] 41 | #define imgPause [UIImage imageNamed:@"video_pause"] 42 | #define imgBack [UIImage imageNamed:@"video_back"] 43 | 44 | /********************** methods ***********************/ 45 | // weakSelf 46 | #define WS(weakSelf) __weak __typeof(&*self) weakSelf = self 47 | // 播放器高度 16:9 48 | #define VideoH(width) (width*9/16.f) 49 | 50 | 51 | /********************** Notifications ***********************/ 52 | // 切换屏幕通知 53 | #define kNotificationChangeScreen @"NotificationChangeScreen" 54 | #define kNotificationPlayVideo @"NotificationPlayVideo" 55 | #define kNotificationPauseVideo @"NotificationPauseVideo" 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | #endif /* HJVideoConst_h */ 64 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoPlayManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayManager.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "HJSingletonService.h" 13 | 14 | #define kVideoPlayerManager [HJVideoPlayManager sharedHJVideoPlayManager] 15 | 16 | #pragma mark - 状态回调 17 | typedef void(^VideoPlayerManagerReadyBlock)(CGFloat totoalDuration);//准备播放回调 18 | typedef void(^VideoPlayerManagerMonitoringBlock)(CGFloat currentDuration);//播放监听回调 19 | typedef void(^VideoPlayerManagerLoadingBlock)(void);//loading回调 20 | typedef void(^VideoPlayerManagerPlayFailedBlock)(void);//播放失败回调 21 | typedef void(^VideoPlayerManagerPlayEndBlock)(void);//播放结束回调 22 | 23 | #pragma mark - 时长回调 24 | typedef void(^VideoPlayerManagerCurrentDurationBlock)(CGFloat currentDuration);//播放时长回调 25 | typedef void(^VideoPlayerManagerTotalDurationBlock)(CGFloat totalDuration);//视频总时长回调 26 | typedef void(^VideoPlayerManagerBufferDurationBlock)(CGFloat bufferDuration);//缓冲时长回调 27 | 28 | @class HJVideoPlayTimeRecord; 29 | 30 | @interface HJVideoPlayManager : NSObject 31 | 32 | @property (nonatomic, strong, readonly) AVPlayer *player; 33 | 34 | /** 最大播放记录数 */ 35 | @property (nonatomic, assign) NSInteger maxRecordCount; 36 | 37 | /** 是否为本地视频*/ 38 | @property (nonatomic, assign, readonly) BOOL isLocalVideo; 39 | 40 | 41 | ServiceSingletonH(HJVideoPlayManager) 42 | 43 | - (void)readyBlock:(VideoPlayerManagerReadyBlock)readyBlock 44 | monitoringBlock:(VideoPlayerManagerMonitoringBlock)monitoringBlock 45 | loadingBlock:(VideoPlayerManagerLoadingBlock)loadingBlock 46 | endBlock:(VideoPlayerManagerPlayEndBlock)endBlock 47 | failedBlock:(VideoPlayerManagerPlayFailedBlock)faildBlock; 48 | 49 | 50 | - (void)totalDurationBlock:(VideoPlayerManagerTotalDurationBlock)totalBlock 51 | currentDurationBlock:(VideoPlayerManagerCurrentDurationBlock)currentBlock 52 | bufferDurationBlock:(VideoPlayerManagerBufferDurationBlock)bufferBlock; 53 | 54 | 55 | - (AVPlayer *)setUrl:(NSString *)url; 56 | 57 | - (void)play; 58 | 59 | - (void)pause; 60 | 61 | - (void)cancelBuffer; 62 | 63 | - (void)seekToTime:(CGFloat)second; 64 | 65 | - (void)reset; 66 | 67 | /** 记录播放时长*/ 68 | - (void)recordUrl:(NSString *)url playTime:(float)playTime; 69 | 70 | /** 获取是否有记录*/ 71 | - (HJVideoPlayTimeRecord *)recordWithUrl:(NSString *)url; 72 | 73 | /** 移除播放记录*/ 74 | - (void)removeRecord:(HJVideoPlayTimeRecord *)record; 75 | 76 | /** 移除所有播放记录*/ 77 | - (void)removeAllPlayRecords; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoPlayManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayManager.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoPlayManager.h" 10 | #import "HJVideoUIManager.h" 11 | #import "HJVideoPlayerHeader.h" 12 | #import "HJVideoConst.h" 13 | #import "HJVideoPlayTimeRecord.h" 14 | @interface HJVideoPlayManager () 15 | // 播放相关 16 | @property (nonatomic ,strong) AVURLAsset *urlAsset; 17 | @property (nonatomic, strong) AVPlayer *player; 18 | @property (nonatomic, strong) AVPlayerItem *playerItem; 19 | @property (nonatomic, strong) NSObject *playbackTimeObserver; 20 | /** 是否为本地视频*/ 21 | @property (nonatomic ,assign) BOOL isLocalVideo; 22 | 23 | /** 缓冲是否已暂停 */ 24 | @property (nonatomic, assign) BOOL bufferPaused; 25 | 26 | 27 | /** 播放地址 */ 28 | @property (nonatomic, copy) NSString * videoUrl; 29 | /** 播放记录 */ 30 | @property (nonatomic, strong) NSMutableArray *playRecords; 31 | 32 | 33 | /** 视频时长(秒)*/ 34 | @property (nonatomic ,assign) CGFloat totalDuration; 35 | /** 当前播放时长*/ 36 | @property (nonatomic ,assign) CGFloat currentDuration; 37 | /** 当前缓冲进度*/ 38 | @property (nonatomic ,assign) CGFloat bufferDuration; 39 | /** 是否正在播放*/ 40 | @property (nonatomic ,assign) BOOL isPlaying; 41 | 42 | //播放状态回调 43 | @property (nonatomic ,copy) VideoPlayerManagerReadyBlock readyBlock; 44 | 45 | @property (nonatomic ,copy) VideoPlayerManagerMonitoringBlock monitoringBlock; 46 | 47 | @property (nonatomic ,copy) VideoPlayerManagerLoadingBlock loadingBlock; 48 | 49 | @property (nonatomic ,copy) VideoPlayerManagerPlayEndBlock endBlock; 50 | 51 | @property (nonatomic ,copy) VideoPlayerManagerPlayFailedBlock failedBlock; 52 | 53 | 54 | //时长回调 55 | @property (nonatomic, copy) VideoPlayerManagerCurrentDurationBlock currentDurationBlock; 56 | 57 | @property (nonatomic, copy) VideoPlayerManagerTotalDurationBlock totalDurationBlock; 58 | 59 | @property (nonatomic, copy) VideoPlayerManagerBufferDurationBlock bufferDurationBlock; 60 | 61 | @end 62 | 63 | @implementation HJVideoPlayManager 64 | 65 | ServiceSingletonM(HJVideoPlayManager) 66 | 67 | 68 | #pragma mark - Public Methods 69 | 70 | - (void)readyBlock:(VideoPlayerManagerReadyBlock)readyBlock 71 | monitoringBlock:(VideoPlayerManagerMonitoringBlock)monitoringBlock 72 | loadingBlock:(VideoPlayerManagerLoadingBlock)loadingBlock 73 | endBlock:(VideoPlayerManagerPlayEndBlock)endBlock 74 | failedBlock:(VideoPlayerManagerPlayFailedBlock)faildBlock{ 75 | 76 | [self setReadyBlock:readyBlock]; 77 | [self setMonitoringBlock:monitoringBlock]; 78 | [self setLoadingBlock:loadingBlock]; 79 | [self setEndBlock:endBlock]; 80 | [self setFailedBlock:faildBlock]; 81 | } 82 | 83 | 84 | - (void)totalDurationBlock:(VideoPlayerManagerTotalDurationBlock)totalBlock 85 | currentDurationBlock:(VideoPlayerManagerCurrentDurationBlock)currentBlock 86 | bufferDurationBlock:(VideoPlayerManagerBufferDurationBlock)bufferBlock;{ 87 | 88 | [self setTotalDurationBlock:totalBlock]; 89 | [self setCurrentDurationBlock:currentBlock]; 90 | [self setBufferDurationBlock:bufferBlock]; 91 | } 92 | 93 | 94 | - (AVPlayer *)setUrl:(NSString *)url{ 95 | if (!url || url.length == 0) return nil; 96 | 97 | //Reset player 98 | [self reset]; 99 | 100 | _videoUrl = url; 101 | 102 | NSURL *urlAddress = nil; 103 | 104 | if ([url hasPrefix:@"http"]) { 105 | urlAddress = [NSURL URLWithString:url]; 106 | [self setIsLocalVideo:NO]; 107 | }else{ 108 | urlAddress = [NSURL fileURLWithPath:url]; 109 | [self setIsLocalVideo:YES]; 110 | } 111 | 112 | self.urlAsset = [AVURLAsset assetWithURL:urlAddress]; 113 | self.playerItem = [AVPlayerItem playerItemWithAsset:self.urlAsset]; 114 | [self.player replaceCurrentItemWithPlayerItem:self.playerItem]; 115 | 116 | [self addObserver]; 117 | 118 | return [self player]; 119 | } 120 | 121 | - (void)play 122 | { 123 | if (self.bufferPaused) { 124 | [self recoverBuffer]; 125 | } 126 | [self.player play]; 127 | [self setIsPlaying:YES]; 128 | [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPlayVideo object:nil]; 129 | NSLog(@"开始播放!"); 130 | } 131 | 132 | 133 | - (void)pause 134 | { 135 | [self.player pause]; 136 | [self setIsPlaying:NO]; 137 | [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationPauseVideo object:nil]; 138 | NSLog(@"暂停播放!"); 139 | } 140 | 141 | 142 | - (void)cancelBuffer{ 143 | 144 | [self.playerItem cancelPendingSeeks]; 145 | [self.playerItem.asset cancelLoading]; 146 | [self.player replaceCurrentItemWithPlayerItem:nil]; 147 | [self setBufferPaused:YES]; 148 | } 149 | 150 | 151 | - (void)recoverBuffer{ 152 | 153 | if (self.playerItem) { 154 | [self.player replaceCurrentItemWithPlayerItem:self.playerItem]; 155 | [self setBufferPaused:NO]; 156 | } 157 | } 158 | 159 | 160 | - (void)seekToTime:(CGFloat)seconds{ 161 | 162 | [self.player pause]; 163 | [self.player seekToTime:CMTimeMakeWithSeconds(seconds, NSEC_PER_SEC) completionHandler:^(BOOL finished) { 164 | if (self.isPlaying) { 165 | [self play]; 166 | } 167 | }]; 168 | } 169 | 170 | 171 | 172 | 173 | #pragma mark - add/remove Observer 174 | - (void)addObserver 175 | { 176 | [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; 177 | [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; 178 | 179 | // 已缓冲 180 | [self.playerItem addObserver:self forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionNew context:nil]; 181 | // 未缓冲 182 | [self.playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; 183 | // 可以播放 184 | [self.playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; 185 | 186 | // 添加视频播放结束通知 187 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem]; 188 | } 189 | 190 | - (void)removeObserver 191 | { 192 | [[self.player currentItem] removeObserver:self forKeyPath:@"status"]; 193 | [[self.player currentItem] removeObserver:self forKeyPath:@"loadedTimeRanges"]; 194 | [[self.player currentItem] removeObserver:self forKeyPath:@"playbackBufferFull" context:nil]; 195 | [[self.player currentItem] removeObserver:self forKeyPath:@"playbackBufferEmpty" context:nil]; 196 | [[self.player currentItem] removeObserver:self forKeyPath:@"playbackLikelyToKeepUp" context:nil]; 197 | 198 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem]; 199 | 200 | if (self.playbackTimeObserver) { 201 | [self.player removeTimeObserver:self.playbackTimeObserver]; 202 | } 203 | } 204 | 205 | #pragma mark - Event Response 206 | 207 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 208 | change:(NSDictionary *)change 209 | context:(void *)context{ 210 | 211 | AVPlayerItem *playerItem = (AVPlayerItem *)object; 212 | if ([keyPath isEqualToString:@"status"]) { 213 | if ([playerItem status] == AVPlayerStatusReadyToPlay) { 214 | NSLog(@"VideoPlayer : [AVPlayerStatusReadyToPlay]"); 215 | [self monitoringPlayback:playerItem];// 给播放器添加计时器 216 | }else if ([playerItem status] == AVPlayerStatusFailed ) { 217 | NSLog(@"VideoPlayer : [AVPlayerStatusFailed]"); 218 | if(self.failedBlock){ 219 | self.failedBlock(); 220 | } 221 | }else if ([playerItem status] == AVPlayerStatusUnknown){ 222 | NSLog(@"VideoPlayer : [AVPlayerStatusUnknown]"); 223 | } 224 | } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { //监听播放器的下载进度 225 | 226 | NSArray *loadedTimeRanges = [playerItem loadedTimeRanges]; 227 | CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域 228 | float startSeconds = CMTimeGetSeconds(timeRange.start); 229 | float durationSeconds = CMTimeGetSeconds(timeRange.duration); 230 | self.bufferDuration = startSeconds + durationSeconds;// 计算缓冲总进度 231 | 232 | } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态 233 | NSLog(@"VideoPlayer : [playbackBufferEmpty]"); 234 | if (playerItem.playbackBufferEmpty) { 235 | //缓冲中 236 | if (self.loadingBlock) { 237 | self.loadingBlock(); 238 | } 239 | } 240 | } else if ([keyPath isEqualToString:@"playbackBufferFull"]) { 241 | 242 | NSLog(@"VideoPlayer : [playbackBufferFull]"); 243 | 244 | }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { 245 | 246 | NSLog(@"VideoPlayer : [playbackLikelyToKeepUp]"); 247 | 248 | } 249 | } 250 | 251 | 252 | - (void)moviePlayDidEnd:(NSNotification *)notif{ 253 | 254 | if (self.endBlock) { 255 | self.endBlock(); 256 | } 257 | } 258 | 259 | #pragma mark - getters / setters 260 | - (void)setCurrentDuration:(CGFloat)currentDuration 261 | { 262 | _currentDuration = currentDuration; 263 | NSLog(@"当前播放时间 %.2f秒",currentDuration); 264 | if (self.currentDurationBlock) { 265 | self.currentDurationBlock(currentDuration); 266 | } 267 | } 268 | 269 | 270 | - (void)setTotalDuration:(CGFloat)totalDuration 271 | { 272 | _totalDuration = totalDuration; 273 | NSLog(@"当前播放时间 %.2f秒",totalDuration); 274 | if(self.totalDurationBlock){ 275 | self.totalDurationBlock(totalDuration); 276 | } 277 | 278 | } 279 | 280 | 281 | - (void)setBufferDuration:(CGFloat)bufferDuration 282 | { 283 | _bufferDuration = bufferDuration; 284 | NSLog(@"当前缓冲时间 %.2f秒",bufferDuration); 285 | if (self.bufferDurationBlock) { 286 | self.bufferDurationBlock(bufferDuration); 287 | } 288 | 289 | } 290 | 291 | - (AVPlayer *)player 292 | { 293 | if(!_player){ 294 | _player = [[AVPlayer alloc]init]; 295 | } 296 | return _player; 297 | } 298 | 299 | - (NSMutableArray *)playRecords{ 300 | 301 | if (!_playRecords) { 302 | _playRecords = [NSMutableArray array]; 303 | } 304 | return _playRecords; 305 | } 306 | 307 | #pragma mark - Private Methods 308 | - (void)monitoringPlayback:(AVPlayerItem *)playerItem 309 | { 310 | //视频总时间 311 | self.totalDuration = (float)playerItem.duration.value / playerItem.duration.timescale; 312 | //视频准备播放回调 313 | if (self.readyBlock) { 314 | self.readyBlock(self.totalDuration); 315 | } 316 | 317 | 318 | __weak __typeof(self)weakSelf = self; 319 | self.playbackTimeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) { 320 | weakSelf.currentDuration = playerItem.currentTime.value/playerItem.currentTime.timescale; 321 | 322 | if(weakSelf.monitoringBlock){ 323 | weakSelf.monitoringBlock(weakSelf.currentDuration); 324 | } 325 | }]; 326 | } 327 | 328 | 329 | - (void)reset 330 | { 331 | [[self.player currentItem] cancelPendingSeeks]; 332 | [[self.player currentItem].asset cancelLoading]; 333 | [self removeObserver]; 334 | [self setUrlAsset:nil]; 335 | [self setPlayerItem:nil]; 336 | [self setPlaybackTimeObserver:nil]; 337 | [self.player replaceCurrentItemWithPlayerItem:nil]; 338 | } 339 | 340 | 341 | #pragma mark - 播放记录 342 | - (void)recordUrl:(NSString *)url playTime:(float)playTime{ 343 | 344 | if(!url || url.length==0) return; 345 | 346 | HJVideoPlayTimeRecord * record = [self recordWithUrl:url]; 347 | if(!record){ 348 | record = [[HJVideoPlayTimeRecord alloc] init]; 349 | } 350 | record.url = url; 351 | record.playTime = playTime; 352 | [self record:record]; 353 | } 354 | 355 | 356 | - (HJVideoPlayTimeRecord *)recordWithUrl:(NSString *)url{ 357 | 358 | __block HJVideoPlayTimeRecord * record = nil; 359 | [self.playRecords enumerateObjectsUsingBlock:^(HJVideoPlayTimeRecord * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 360 | if ([obj.url isEqualToString:url]) { 361 | record = obj; 362 | * stop = YES; 363 | } 364 | }]; 365 | 366 | return record; 367 | } 368 | 369 | 370 | - (void)record:(HJVideoPlayTimeRecord *)record{ 371 | 372 | if (![self.playRecords containsObject:record]) { 373 | if (self.playRecords.count == self.maxRecordCount && self.playRecords.count != 0) { 374 | [self.playRecords removeObjectAtIndex:0]; 375 | } 376 | [self.playRecords addObject:record]; 377 | } 378 | } 379 | 380 | 381 | - (void)removeRecord:(HJVideoPlayTimeRecord *)record{ 382 | 383 | if ([self.playRecords containsObject:record]) { 384 | [self.playRecords removeObject:record]; 385 | } 386 | } 387 | 388 | - (void)removeAllPlayRecords{ 389 | 390 | [self.playRecords removeAllObjects]; 391 | } 392 | @end 393 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoPlayerUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerUtil.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/29. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HJVideoPlayerUtil : NSObject 13 | 14 | /** 获取视频第一帧图片 */ 15 | + (UIImage *)getImageWithVideoUrl:(NSString *)urlString; 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoPlayerUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerUtil.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/29. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoPlayerUtil.h" 10 | #import 11 | #import 12 | #import 13 | 14 | @implementation HJVideoPlayerUtil 15 | 16 | + (UIImage *)getImageWithVideoUrl:(NSString *)urlString{ 17 | 18 | //视频地址 19 | NSURL *url = nil; 20 | if ([urlString hasPrefix:@"http"]) { 21 | url = [NSURL URLWithString:urlString]; 22 | }else{ 23 | url = [NSURL fileURLWithPath:urlString]; 24 | } 25 | 26 | AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:nil];// 27 | 28 | //获取视频时长,单位:秒 29 | NSLog(@"%llu",urlAsset.duration.value/urlAsset.duration.timescale); 30 | 31 | AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset]; 32 | 33 | generator.appliesPreferredTrackTransform = YES; 34 | 35 | generator.maximumSize = CGSizeMake(1136, 640); 36 | 37 | NSError *error = nil; 38 | 39 | CGImageRef img = [generator copyCGImageAtTime:CMTimeMake(10, 10) actualTime:NULL error:&error]; 40 | 41 | UIImage *image = [UIImage imageWithCGImage: img]; 42 | 43 | return image; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoTimeUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoTimeUtil.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/27. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HJVideoTimeUtil : NSObject 13 | 14 | // 将秒数转换为时分秒字符串 15 | + (NSString *)hmsStringWithFloat:(CGFloat)seconds; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoTimeUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoTimeUtil.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/27. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoTimeUtil.h" 10 | 11 | @implementation HJVideoTimeUtil 12 | 13 | // 将秒数转换为时分秒字符串 14 | + (NSString *)hmsStringWithFloat:(CGFloat)seconds{ 15 | 16 | NSInteger hour = floor(seconds/60/60); 17 | NSInteger minutes = floor(seconds/60)-hour*60; 18 | NSInteger second = seconds - hour*60*60 - minutes*60; 19 | NSString * timeString = nil; 20 | if (hour == 0) { 21 | timeString = [NSString stringWithFormat:@"%02zd:%02zd", minutes, second]; 22 | }else{ 23 | timeString = [NSString stringWithFormat:@"%02zd:%02zd:%02zd", hour, minutes, second]; 24 | } 25 | 26 | return timeString; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoUIManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoUIManager.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/31. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HJSingletonService.h" 11 | 12 | #define kHJVideoUIManager [HJVideoUIManager sharedHJVideoUIManager] 13 | 14 | @interface HJVideoUIManager : NSObject 15 | 16 | ServiceSingletonH(HJVideoUIManager) 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJVideoUIManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoUIManager.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/31. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoUIManager.h" 10 | 11 | @implementation HJVideoUIManager 12 | 13 | ServiceSingletonM(HJVideoUIManager) 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJViewFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJViewFactory.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/18. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "UIImage+getImage.h" 11 | 12 | @interface HJViewFactory : NSObject 13 | 14 | 15 | + (UIButton *)buttonWithNormalImage:(UIImage *)normalImage 16 | selectedImage:(UIImage *)selectedImage; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/HJViewFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJViewFactory.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/18. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJViewFactory.h" 10 | 11 | 12 | @implementation HJViewFactory 13 | 14 | 15 | 16 | + (UIButton *)buttonWithNormalImage:(UIImage *)normalImage 17 | selectedImage:(UIImage *)selectedImage;{ 18 | 19 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 20 | [btn setShowsTouchWhenHighlighted:YES]; 21 | 22 | if(normalImage) 23 | [btn setImage:normalImage forState:UIControlStateNormal]; 24 | 25 | if (selectedImage) 26 | [btn setImage:selectedImage forState:UIControlStateSelected]; 27 | 28 | [btn setImageEdgeInsets:UIEdgeInsetsMake(8, 8, 8, 8)]; 29 | 30 | return btn; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/UIDevice+HJVideoRotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+HJVideoRotation.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/31. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIDevice (HJVideoRotation) 12 | 13 | + (void)rotateToOrientation:(UIInterfaceOrientation)orientation; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/UIDevice+HJVideoRotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+HJVideoRotation.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/31. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "UIDevice+HJVideoRotation.h" 10 | 11 | @implementation UIDevice (HJVideoRotation) 12 | 13 | + (void)rotateToOrientation:(UIInterfaceOrientation)orientation{ 14 | // 设置方向 15 | NSNumber *orientationTarget = [NSNumber numberWithInt:orientation]; 16 | [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/UIImage+getImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+getImage.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIImage (getImage) 12 | 13 | 14 | + (UIImage *)imageFromBundleWithName:(NSString *)imageName; 15 | 16 | 17 | - (UIImage *)imageWithSize:(CGSize)size; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Others/UIImage+getImage.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // UIImage+getImage.m 4 | // HJVideoPlayer 5 | // 6 | // Created by WHJ on 16/10/17. 7 | // Copyright © 2016年 WHJ. All rights reserved. 8 | // 9 | 10 | #import "UIImage+getImage.h" 11 | 12 | @implementation UIImage (getImage) 13 | 14 | 15 | + (UIImage *)imageFromBundleWithName:(NSString *)imageName{ 16 | 17 | NSString *path = [[NSBundle mainBundle] pathForResource:@"videoImages" ofType:@".bundle"]; 18 | NSString *fullImageName = [path stringByAppendingPathComponent:imageName]; 19 | return [UIImage imageNamed:fullImageName]; 20 | } 21 | 22 | 23 | - (UIImage *)imageWithSize:(CGSize)size;{ 24 | 25 | UIGraphicsBeginImageContext(size); 26 | [self drawInRect:CGRectMake(0, 0, size.width, size.height)]; 27 | UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 28 | UIGraphicsEndImageContext(); 29 | return newImage; 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/Back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/Back@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/Back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/Back@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_back@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_back@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_bg.jpg -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_farword_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_farword_left.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_farword_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_farword_right.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_pause@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_pause@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_pause@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_play@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_play@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_play@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_replay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_replay@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_replay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_replay@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_slider_thumb@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_slider_thumb@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_slider_thumb@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_slider_thumb@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen_white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen_white@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen_white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toFullScreen_white@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toHalfScreen_white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toHalfScreen_white@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toHalfScreen_white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/video_toHalfScreen_white@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/最小化-按下@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/最小化-按下@2x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Resources/videoImages.bundle/最小化-按下@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WuHuijian/HJVideoPlayer/46085e15e079e98bf0f9035dd9ead5a58bed081a/HJVideoPlayer/VideoClass/Resources/videoImages.bundle/最小化-按下@3x.png -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJBufferSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJBufferSlider.h 3 | // HJBufferSlider 4 | // 5 | // Created by WHJ on 2016/11/22. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJBufferSlider : UIView 12 | 13 | @property (nonatomic, assign) CGFloat minimumValue; 14 | 15 | @property (nonatomic, assign) CGFloat maximumValue; 16 | 17 | @property (nonatomic, assign) CGFloat bufferValue;//缓冲进度值 18 | 19 | @property (nonatomic, assign) CGFloat progressValue;//进度值 20 | 21 | // Color 22 | 23 | @property (nonatomic, strong) UIColor * progressTrackColor;//进度颜色 24 | 25 | @property (nonatomic, strong) UIColor * bufferTrackColor;//缓冲进度颜色 26 | 27 | @property (nonatomic, strong) UIColor * maxBufferTrackColor;//背景色 28 | 29 | @property (nonatomic, strong) UIColor * thumbTintColor;//滑块颜色 30 | 31 | 32 | // Image 33 | 34 | @property (nonatomic, strong) UIImage * progressTrackImage;//进度图片 35 | 36 | @property (nonatomic, strong) UIImage * bufferTrackImage;//缓冲进度图片 37 | 38 | @property (nonatomic, strong) UIImage * maxBufferTrackImage;//背景图片 39 | 40 | @property (nonatomic, strong) UIImage * thumbImage;//滑块图片 41 | 42 | 43 | // Methods 44 | - (void)addTarget:(nullable id)target action:(nonnull SEL)sel 45 | forControlEvents:(UIControlEvents)controlEvent; 46 | 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJBufferSlider.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJBufferSlider.m 3 | // HJBufferSlider 4 | // 5 | // Created by WHJ on 2016/11/22. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJBufferSlider.h" 10 | 11 | 12 | @interface HJBufferSlider () 13 | 14 | @property (nonatomic, strong) UISlider *progressSlider; 15 | 16 | @property (nonatomic, strong) UISlider *bufferSlider; 17 | 18 | /** 缓冲slider背景色 */ 19 | @property (nonatomic, strong) UIColor *bufferBgColor; 20 | 21 | @end 22 | 23 | @implementation HJBufferSlider 24 | 25 | 26 | - (instancetype)init{ 27 | self = [super init]; 28 | if (self) { 29 | [self setupUI]; 30 | } 31 | return self; 32 | } 33 | 34 | -(instancetype)initWithFrame:(CGRect)frame;{ 35 | self = [super initWithFrame:frame]; 36 | if(self){ 37 | [self setupUI]; 38 | } 39 | return self; 40 | } 41 | 42 | 43 | #pragma mark - About UI 44 | - (void)setupUI{ 45 | 46 | UISlider *bufferSlider = [[UISlider alloc]init]; 47 | [bufferSlider setUserInteractionEnabled:NO]; 48 | [bufferSlider setThumbTintColor:[UIColor clearColor]]; 49 | [self setBufferSlider:bufferSlider]; 50 | [self addSubview:bufferSlider]; 51 | 52 | 53 | UISlider *progressSlider = [[UISlider alloc]init]; 54 | [progressSlider setMaximumTrackTintColor:[UIColor clearColor]]; 55 | [self setProgressSlider:progressSlider]; 56 | [self addSubview:progressSlider]; 57 | } 58 | 59 | 60 | - (void)layoutSubviews{ 61 | 62 | [super layoutSubviews]; 63 | [self.bufferSlider setFrame:self.bounds]; 64 | [self.progressSlider setFrame:self.bounds]; 65 | } 66 | 67 | 68 | #pragma mark - Public Methods 69 | 70 | - (void)addTarget:(nullable id)target action:(nonnull SEL)sel 71 | forControlEvents:(UIControlEvents)controlEvent;{ 72 | 73 | [self.progressSlider addTarget:target action:sel forControlEvents:controlEvent]; 74 | 75 | } 76 | 77 | 78 | 79 | 80 | #pragma mark - Setters/Getters 81 | 82 | - (void)setMinimumValue:(CGFloat)minimumValue{ 83 | 84 | _minimumValue = minimumValue; 85 | [self.progressSlider setMinimumValue:minimumValue]; 86 | [self.bufferSlider setMinimumValue:minimumValue]; 87 | } 88 | 89 | - (void)setMaximumValue:(CGFloat)maximumValue{ 90 | 91 | _maximumValue = maximumValue; 92 | [self.progressSlider setMaximumValue:maximumValue]; 93 | [self.bufferSlider setMaximumValue:maximumValue]; 94 | } 95 | 96 | - (void)setFrame:(CGRect)frame{ 97 | 98 | [super setFrame:frame]; 99 | [self.bufferSlider setFrame:self.bounds]; 100 | [self.progressSlider setFrame:self.bounds]; 101 | } 102 | /** 103 | * Buffer Slider 104 | */ 105 | 106 | - (void)setBufferValue:(CGFloat)bufferValue{ 107 | 108 | _bufferValue = bufferValue; 109 | [self.bufferSlider setValue:bufferValue]; 110 | //临时解决缓冲完成但是进度条不充满问题 111 | if(self.bufferSlider.maximumValue == bufferValue){ 112 | [self setMaxBufferTrackColor:self.bufferSlider.minimumTrackTintColor]; 113 | }else{ 114 | [self setMaxBufferTrackColor:self.bufferBgColor]; 115 | } 116 | } 117 | 118 | - (void)setBufferTrackColor:(UIColor *)bufferTrackColor{ 119 | 120 | _bufferTrackColor = bufferTrackColor; 121 | [self.bufferSlider setMinimumTrackTintColor:bufferTrackColor]; 122 | } 123 | 124 | 125 | - (void)setMaxBufferTrackColor:(UIColor *)maxBufferTrackColor{ 126 | 127 | _maxBufferTrackColor = maxBufferTrackColor; 128 | [self.bufferSlider setMaximumTrackTintColor:maxBufferTrackColor]; 129 | if (!self.bufferBgColor) { 130 | self.bufferBgColor = maxBufferTrackColor; 131 | } 132 | } 133 | 134 | 135 | - (void)setBufferTrackImage:(UIImage *)bufferTrackImage{ 136 | 137 | _bufferTrackImage = bufferTrackImage; 138 | [self.bufferSlider setMinimumTrackImage:bufferTrackImage forState:UIControlStateNormal]; 139 | } 140 | 141 | 142 | - (void)setMaxBufferTrackImage:(UIImage *)maxBufferTrackImage{ 143 | 144 | _maxBufferTrackImage = maxBufferTrackImage; 145 | [self.bufferSlider setMaximumTrackImage:maxBufferTrackImage forState:UIControlStateNormal]; 146 | } 147 | /** 148 | * Progress Slider 149 | */ 150 | - (void)setProgressValue:(CGFloat)progressValue{ 151 | 152 | _progressValue = progressValue; 153 | [self.progressSlider setValue:progressValue]; 154 | } 155 | 156 | 157 | - (void)setProgressTrackColor:(UIColor *)progressTrackColor{ 158 | 159 | _progressTrackColor = progressTrackColor; 160 | [self.progressSlider setMinimumTrackTintColor:progressTrackColor]; 161 | } 162 | 163 | 164 | - (void)setThumbTintColor:(UIColor *)thumbTintColor{ 165 | 166 | _thumbTintColor = thumbTintColor; 167 | [self.progressSlider setThumbTintColor:thumbTintColor]; 168 | } 169 | 170 | 171 | - (void)setProgressTrackImage:(UIImage *)progressTrackImage{ 172 | 173 | _progressTrackImage = progressTrackImage; 174 | [self.progressSlider setMinimumTrackImage:progressTrackImage forState:UIControlStateNormal]; 175 | } 176 | 177 | - (void)setThumbImage:(UIImage *)thumbImage{ 178 | 179 | _thumbImage = thumbImage; 180 | [self.progressSlider setThumbImage:thumbImage forState:UIControlStateNormal]; 181 | } 182 | 183 | 184 | 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJCircleLoading.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJCircleLoading.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/16. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJCircleLoading : UIView 12 | 13 | /** 直径 */ 14 | @property (nonatomic, assign) CGFloat circleDiameter; 15 | /** 一次动画时长 */ 16 | @property (nonatomic, assign) CGFloat duration; 17 | /** 画线颜色 */ 18 | @property (nonatomic, strong) UIColor *strokeColor; 19 | /** 线条宽度 */ 20 | @property (nonatomic, assign) CGFloat lineWidth; 21 | /** layer背景色 */ 22 | @property (nonatomic, strong) UIColor *fillColor; 23 | 24 | - (void)startAnimating; 25 | 26 | - (void)stopAnimating; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJCircleLoading.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJCircleLoading.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/16. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJCircleLoading.h" 10 | 11 | static NSString * const kCircleLoadingKey = @"CircleLoadingKey"; 12 | static const CGFloat kDefaultCircleDiameter = 40.f; 13 | 14 | @interface HJCircleLoading () 15 | 16 | @property (nonatomic, strong) CAShapeLayer *shapeLayer; 17 | 18 | @end 19 | 20 | @implementation HJCircleLoading 21 | 22 | - (instancetype)init 23 | { 24 | self = [super init]; 25 | if (self) { 26 | [self buildSomeParams]; 27 | } 28 | return self; 29 | } 30 | 31 | 32 | 33 | -(instancetype)initWithFrame:(CGRect)frame;{ 34 | self = [super initWithFrame:frame]; 35 | if(self){ 36 | [self buildSomeParams]; 37 | } 38 | return self; 39 | } 40 | 41 | 42 | - (void)buildSomeParams{ 43 | 44 | self.circleDiameter = kDefaultCircleDiameter; 45 | self.duration = 1.2f; 46 | self.strokeColor = [UIColor whiteColor]; 47 | self.lineWidth = 4; 48 | self.fillColor = [UIColor clearColor]; 49 | } 50 | 51 | - (void)startAnimating{ 52 | 53 | //layer配置 54 | CGFloat margin = (self.frame.size.width - self.circleDiameter)/2.f; 55 | self.shapeLayer.frame = CGRectMake(margin, margin, self.circleDiameter, self.circleDiameter); 56 | self.shapeLayer.strokeColor = self.strokeColor.CGColor; 57 | self.shapeLayer.fillColor = self.fillColor.CGColor; 58 | self.shapeLayer.lineWidth = self.lineWidth; 59 | 60 | //设置动画路径 61 | UIBezierPath * circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.circleDiameter, self.circleDiameter)]; 62 | self.shapeLayer.path = circlePath.CGPath; 63 | 64 | //设置出现动画 65 | CABasicAnimation *startAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; 66 | startAnimation.fromValue = @0; 67 | startAnimation.toValue = @1; 68 | startAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 69 | startAnimation.duration = self.duration; 70 | 71 | //设置结束动画 72 | CABasicAnimation *endAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 73 | endAnimation.fromValue = @0; 74 | endAnimation.toValue = @1; 75 | endAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 76 | endAnimation.duration = self.duration * 0.5; 77 | 78 | //组动画 79 | CAAnimationGroup *strokeAniamtionGroup = [CAAnimationGroup animation]; 80 | strokeAniamtionGroup.duration = self.duration; 81 | strokeAniamtionGroup.animations = @[endAnimation,startAnimation]; 82 | strokeAniamtionGroup.repeatCount = MAXFLOAT; 83 | [self.shapeLayer addAnimation:strokeAniamtionGroup forKey:kCircleLoadingKey]; 84 | 85 | } 86 | 87 | 88 | - (void)stopAnimating{ 89 | if ([self.shapeLayer.animationKeys containsObject:kCircleLoadingKey]) { 90 | [self.shapeLayer removeAnimationForKey:kCircleLoadingKey]; 91 | } 92 | } 93 | 94 | 95 | 96 | 97 | - (CAShapeLayer *)shapeLayer{ 98 | if (!_shapeLayer) { 99 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 100 | [self.layer addSublayer:shapeLayer]; 101 | _shapeLayer = shapeLayer; 102 | } 103 | return _shapeLayer; 104 | } 105 | 106 | 107 | - (void)setFrame:(CGRect)frame{ 108 | 109 | if (frame.size.widthself.frame.size.width && self.frame.size.width!=0) { 124 | _circleDiameter = self.frame.size.width; 125 | }else{ 126 | _circleDiameter = kDefaultCircleDiameter; 127 | } 128 | } 129 | 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJFastForwardView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJFastForwardView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJFastForwardView : UIView 12 | 13 | /** 最大进度 */ 14 | @property (nonatomic, assign) CGFloat maxDuration; 15 | 16 | /** 当前值 */ 17 | @property (nonatomic, assign, readonly) CGFloat currentDuration; 18 | 19 | /** 进度 */ 20 | @property (nonatomic, assign) CGFloat progress; 21 | 22 | - (void)configForwardLeftImage:(UIImage *)leftImage forwardRightImage:(UIImage *)rightImage; 23 | 24 | - (void)moveRight:(BOOL)isRight; 25 | @end 26 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJFastForwardView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJFastForwardView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJFastForwardView.h" 10 | #import "HJVideoTimeUtil.h" 11 | 12 | @interface HJFastForwardView () 13 | 14 | /** 图标 */ 15 | @property (nonatomic, strong) UIImageView *imageView; 16 | 17 | /** 进度时长 */ 18 | @property (nonatomic, strong) UILabel *progressLabel; 19 | 20 | /** 进度条 */ 21 | @property (nonatomic, strong) UIProgressView * progressView; 22 | 23 | /** 当前值 */ 24 | @property (nonatomic, assign) CGFloat currentDuration; 25 | 26 | @property (nonatomic, strong) UIImage *leftImage; 27 | 28 | @property (nonatomic, strong) UIImage *rightImage; 29 | 30 | /** */ 31 | @property (nonatomic, assign) CGRect fullFrame; 32 | 33 | @property (nonatomic, assign) CGRect halfFrame; 34 | 35 | @end 36 | 37 | @implementation HJFastForwardView 38 | 39 | #pragma mark - Life Circle 40 | -(instancetype)initWithFrame:(CGRect)frame;{ 41 | self = [super initWithFrame:frame]; 42 | if(self){ 43 | 44 | [self setupUI]; 45 | } 46 | return self; 47 | } 48 | 49 | - (instancetype)init 50 | { 51 | self = [super init]; 52 | if (self) { 53 | [self setupUI]; 54 | } 55 | return self; 56 | } 57 | 58 | #pragma mark - About UI 59 | - (void)setupUI{ 60 | 61 | UIImageView *imageView = [[UIImageView alloc] init]; 62 | imageView.backgroundColor = [UIColor clearColor]; 63 | imageView.contentMode = UIViewContentModeScaleAspectFit; 64 | self.imageView = imageView; 65 | [self addSubview:imageView]; 66 | 67 | UILabel *progressLabel = [[UILabel alloc] init]; 68 | progressLabel.backgroundColor = [UIColor clearColor]; 69 | progressLabel.textColor = [UIColor whiteColor]; 70 | progressLabel.textAlignment = NSTextAlignmentCenter; 71 | progressLabel.font = [UIFont systemFontOfSize:10.f]; 72 | self.progressLabel = progressLabel; 73 | [self addSubview:progressLabel]; 74 | 75 | UIProgressView *progressView = [[UIProgressView alloc] init]; 76 | progressView.trackTintColor = [UIColor whiteColor]; 77 | progressView.progressTintColor = [UIColor redColor]; 78 | self.progressView = progressView; 79 | [self addSubview:self.progressView]; 80 | } 81 | 82 | - (void)layoutSubviews{ 83 | 84 | CGFloat width = CGRectGetWidth(self.frame); 85 | CGFloat height = CGRectGetHeight(self.frame); 86 | 87 | self.imageView.frame = CGRectMake(0, 0, width/2.f, height/4.f); 88 | self.imageView.center = CGPointMake(width/2.f, height/4.f); 89 | 90 | self.progressLabel.frame = CGRectMake(0, height/2.f, width, height/4.f); 91 | 92 | self.progressView.frame = CGRectMake(5,(height+height*3/4.f-4)/2.f, width - 10.f, 4.f); 93 | } 94 | 95 | 96 | #pragma mark - Pravite Method 97 | 98 | #pragma mark - Public Method 99 | - (void)configForwardLeftImage:(UIImage *)leftImage forwardRightImage:(UIImage *)rightImage;{ 100 | self.leftImage = leftImage; 101 | self.rightImage = rightImage; 102 | } 103 | 104 | - (void)moveRight:(BOOL)isRight{ 105 | 106 | self.imageView.image = isRight?self.rightImage:self.leftImage; 107 | } 108 | 109 | #pragma mark - Event response 110 | 111 | #pragma mark - Delegate methods 112 | 113 | #pragma mark - Getters/Setters/Lazy 114 | - (void)setProgress:(CGFloat)progress{ 115 | _progress = progress; 116 | 117 | [self.progressView setProgress:progress]; 118 | 119 | self.currentDuration = self.maxDuration * progress; 120 | 121 | [self.progressLabel setText:[NSString stringWithFormat:@"%@/%@",[HJVideoTimeUtil hmsStringWithFloat:self.currentDuration],[HJVideoTimeUtil hmsStringWithFloat:self.maxDuration]]]; 122 | } 123 | 124 | 125 | - (void)setFrame:(CGRect)frame{ 126 | 127 | [super setFrame:frame]; 128 | 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJPlayerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJPlayerView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/11/9. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface HJPlayerView : UIImageView 13 | 14 | @property (nonatomic ,strong) AVPlayer *player; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJPlayerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJPlayerView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/11/9. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJPlayerView.h" 10 | 11 | @implementation HJPlayerView 12 | 13 | + (Class)layerClass { 14 | return [AVPlayerLayer class]; 15 | } 16 | 17 | - (AVPlayer *)player { 18 | AVPlayerLayer *layer = (AVPlayerLayer *)[self layer]; 19 | [layer setVideoGravity: AVLayerVideoGravityResize]; 20 | return [layer player]; 21 | } 22 | 23 | - (void)setPlayer:(AVPlayer *)player { 24 | AVPlayerLayer *layer = (AVPlayerLayer *)[self layer]; 25 | [layer setVideoGravity: AVLayerVideoGravityResize]; 26 | [layer setPlayer:player]; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoBottomView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoBottomView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | 14 | typedef void(^videoPlayerFullScreenBlock)(BOOL isFull); 15 | typedef void(^videoPlayerSliderValueChanged)(float value); 16 | 17 | @class HJVideoConfigModel; 18 | @interface HJVideoBottomView : UIView 19 | 20 | /** 进度*/ 21 | @property (nonatomic, assign ,readonly) CGFloat progressValue; 22 | /** 缓冲进度*/ 23 | @property (nonatomic, assign ,readonly) CGFloat bufferValue; 24 | /** 最大进度 */ 25 | @property (nonatomic, assign ,readonly) CGFloat maximumValue; 26 | /** 大小屏切换回调*/ 27 | @property (nonatomic, copy) videoPlayerFullScreenBlock fullScreenBlock; 28 | /** 进度条拖动回调*/ 29 | @property (nonatomic, copy) videoPlayerSliderValueChanged valueChangedBlock; 30 | /** 是否仅显示slider */ 31 | @property (nonatomic, assign, readonly) BOOL onlySlider; 32 | 33 | @property (nonatomic, strong) HJVideoConfigModel *configModel; 34 | 35 | 36 | // 显示底部视图 37 | - (void)show; 38 | 39 | // 隐藏底部视图 40 | - (void)hide; 41 | 42 | // 设置进度 43 | - (void)setProgress:(CGFloat)progressValue; 44 | 45 | // 设置缓冲进度 46 | - (void)setBufferValue:(CGFloat)bufferValue; 47 | 48 | // 设置最大进度 49 | - (void)setMaximumValue:(CGFloat)value; 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoBottomView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoBottomView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoBottomView.h" 10 | #import "HJVideoPlayManager.h" 11 | #import "HJBufferSlider.h" 12 | #import "HJVideoTimeUtil.h" 13 | #import "HJVideoPlayerHeader.h" 14 | #import "HJVideoConst.h" 15 | #import "HJVideoConfigModel.h" 16 | #import "HJViewFactory.h" 17 | 18 | static const CGFloat kSliderHeight = 4.f; 19 | static const CGFloat kTimeLabelWidth = 50.f; 20 | static const CGFloat kTimeLabelFontSize = 12.f; 21 | 22 | @interface HJVideoBottomView () 23 | 24 | /** 全屏按钮 */ 25 | @property (nonatomic ,strong) UIButton *fullScreenBtn; 26 | /** 播放时长Label */ 27 | @property (nonatomic ,strong) UILabel *playTimeLbl; 28 | /** 总时长Label */ 29 | @property (nonatomic ,strong) UILabel *totalDurationLbl; 30 | /** 缓冲进度条 */ 31 | @property (nonatomic, strong) HJBufferSlider *bufferSlider; 32 | /** 是否仅显示slider */ 33 | @property (nonatomic, assign) BOOL onlySlider; 34 | /** 是否全屏 */ 35 | @property (nonatomic, assign) BOOL fullScreen; 36 | 37 | /** 渐变layer */ 38 | @property (nonatomic, strong) CAGradientLayer* gradientLayer; 39 | 40 | 41 | 42 | @end 43 | 44 | @implementation HJVideoBottomView 45 | 46 | 47 | - (instancetype)initWithFrame:(CGRect)frame;{ 48 | self = [super initWithFrame:frame]; 49 | if(self){ 50 | 51 | [self addNotification]; 52 | 53 | [self setupUI]; 54 | } 55 | return self; 56 | } 57 | 58 | - (instancetype)init 59 | { 60 | self = [super init]; 61 | if (self) { 62 | 63 | [self addNotification]; 64 | 65 | [self setupUI]; 66 | 67 | } 68 | return self; 69 | } 70 | 71 | 72 | 73 | 74 | - (void)addNotification 75 | { 76 | //屏幕切换通知 77 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeScreenAction:) name:kNotificationChangeScreen object:nil]; 78 | 79 | } 80 | 81 | 82 | 83 | - (void)setupUI 84 | { 85 | [self.layer addSublayer:self.gradientLayer]; 86 | 87 | [self addSubview:self.playTimeLbl]; 88 | 89 | [self addSubview:self.totalDurationLbl]; 90 | 91 | [self addSubview:self.fullScreenBtn]; 92 | 93 | [self addSubview:self.bufferSlider]; 94 | } 95 | 96 | - (void)showOnlySlider:(BOOL)show{ 97 | 98 | [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 99 | [obj setHidden:show]; 100 | }]; 101 | [self.bufferSlider setHidden:NO]; 102 | } 103 | 104 | 105 | - (void)refreshUI{ 106 | 107 | CGFloat width = CGRectGetWidth(self.frame); 108 | CGFloat height = CGRectGetHeight(self.frame); 109 | 110 | if (self.configModel && self.configModel.onlyFullScreen && !self.onlySlider){ 111 | [self showOnlySlider:NO]; 112 | 113 | self.fullScreenBtn.frame = CGRectMake(width, 0, height, height); 114 | 115 | [self adjustSubviesOnlyFull:YES]; 116 | 117 | }else if (!self.onlySlider && !self.fullScreen){ 118 | 119 | [self showOnlySlider:NO]; 120 | 121 | self.fullScreenBtn.frame = CGRectMake(width-height, 0, height, height); 122 | 123 | [self adjustSubviesOnlyFull:NO]; 124 | 125 | }else if (!self.onlySlider && self.fullScreen) { 126 | 127 | [self showOnlySlider:NO]; 128 | 129 | self.fullScreenBtn.frame = CGRectMake(width-height, 0, height, height); 130 | 131 | [self adjustSubviesOnlyFull:NO]; 132 | 133 | }else{ 134 | 135 | [self showOnlySlider:YES]; 136 | } 137 | 138 | } 139 | 140 | - (void)adjustSubviesOnlyFull:(BOOL)onlyFull{ 141 | 142 | CGFloat width = CGRectGetWidth(self.frame); 143 | CGFloat height = CGRectGetHeight(self.frame); 144 | 145 | self.playTimeLbl.frame = CGRectMake(0, 0, kTimeLabelWidth, height); 146 | 147 | self.totalDurationLbl.frame = CGRectMake(width - (onlyFull?0:height) - kTimeLabelWidth, 0, kTimeLabelWidth, height); 148 | 149 | self.bufferSlider.frame = CGRectMake(CGRectGetMaxX(self.playTimeLbl.frame), (height - kSliderHeight)/2.f, CGRectGetMinX(self.totalDurationLbl.frame)-CGRectGetMaxX(self.playTimeLbl.frame), kSliderHeight); 150 | } 151 | 152 | 153 | - (void)changeGradientRect{ 154 | 155 | self.gradientLayer.frame = self.bounds; 156 | //设置渐变区域的起始和终止位置(范围为0-1) 157 | self.gradientLayer.startPoint = CGPointMake(0, 0); 158 | self.gradientLayer.endPoint = CGPointMake(0, 1); 159 | //设置颜色数组 160 | self.gradientLayer.colors = @[(__bridge id)[UIColor clearColor].CGColor, 161 | (__bridge id)kVideoBottomGradientColor.CGColor]; 162 | //设置颜色分割点(范围:0-1) 163 | self.gradientLayer.locations = @[@(0.f), @(1.0f)]; 164 | } 165 | 166 | #define mark - getters /setters 167 | 168 | 169 | - (UIButton *)fullScreenBtn 170 | { 171 | if (!_fullScreenBtn) { 172 | _fullScreenBtn = [HJViewFactory buttonWithNormalImage:imgFullScreen selectedImage:imgHalfScreen]; 173 | [_fullScreenBtn addTarget:self action:@selector(fullOrHalfScreenAction:) forControlEvents:UIControlEventTouchUpInside]; 174 | } 175 | return _fullScreenBtn; 176 | } 177 | 178 | - (UILabel *)playTimeLbl 179 | { 180 | if (!_playTimeLbl) { 181 | _playTimeLbl = [[UILabel alloc]init]; 182 | [_playTimeLbl setBackgroundColor:[UIColor clearColor]]; 183 | [_playTimeLbl setText:@"00:00"]; 184 | [_playTimeLbl setTextColor:[UIColor whiteColor]]; 185 | [_playTimeLbl setTextAlignment:NSTextAlignmentRight]; 186 | [_playTimeLbl setTextAlignment:NSTextAlignmentCenter]; 187 | [_playTimeLbl setFont:[UIFont systemFontOfSize:kTimeLabelFontSize]]; 188 | } 189 | return _playTimeLbl; 190 | } 191 | 192 | 193 | - (UILabel *)totalDurationLbl 194 | { 195 | if (!_totalDurationLbl) { 196 | _totalDurationLbl = [[UILabel alloc]init]; 197 | [_totalDurationLbl setBackgroundColor:[UIColor clearColor]]; 198 | [_totalDurationLbl setText:@"00:00"]; 199 | [_totalDurationLbl setTextColor:[UIColor whiteColor]]; 200 | [_totalDurationLbl setTextAlignment:NSTextAlignmentCenter]; 201 | [_totalDurationLbl setFont:[UIFont systemFontOfSize:kTimeLabelFontSize]]; 202 | } 203 | return _totalDurationLbl; 204 | } 205 | 206 | - (HJBufferSlider *)bufferSlider 207 | { 208 | if (!_bufferSlider) { 209 | _bufferSlider = [[HJBufferSlider alloc]initWithFrame:CGRectMake(0, -kSliderHeight/2.f, CGRectGetWidth(self.frame), kSliderHeight)]; 210 | [_bufferSlider setProgressTrackColor:[UIColor yellowColor]]; 211 | [_bufferSlider setBufferTrackColor:[UIColor whiteColor]]; 212 | [_bufferSlider setMaxBufferTrackColor:[UIColor darkGrayColor]]; 213 | [_bufferSlider setThumbImage:[imgSliderThumb imageWithSize:CGSizeMake(8, 8)]]; 214 | [_bufferSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 215 | } 216 | return _bufferSlider; 217 | } 218 | 219 | - (CAGradientLayer *)gradientLayer{ 220 | if (!_gradientLayer) { 221 | _gradientLayer = [[CAGradientLayer alloc] init]; 222 | } 223 | return _gradientLayer; 224 | } 225 | 226 | - (CGFloat)progressValue 227 | { 228 | return self.bufferSlider.progressValue; 229 | } 230 | 231 | 232 | - (CGFloat)bufferValue 233 | { 234 | return self.bufferSlider.bufferValue; 235 | } 236 | 237 | - (CGFloat)maximumValue 238 | { 239 | return self.bufferSlider.maximumValue; 240 | } 241 | 242 | 243 | #pragma mark - Event Response 244 | 245 | - (void)fullOrHalfScreenAction:(UIButton *)sender{ 246 | sender.selected = !sender.selected; 247 | if (self.fullScreenBlock) { 248 | self.fullScreenBlock(sender.selected); 249 | } 250 | } 251 | 252 | - (void)sliderValueChanged:(UISlider *)sender{ 253 | 254 | if(self.valueChangedBlock){ 255 | self.valueChangedBlock(sender.value); 256 | } 257 | } 258 | 259 | 260 | - (void)changeScreenAction:(NSNotification *)notif{ 261 | 262 | BOOL isFullScreen = [notif.object boolValue]; 263 | 264 | [self setFullScreen:isFullScreen]; 265 | // 修改切换屏幕按钮图片 266 | [self.fullScreenBtn setSelected:isFullScreen]; 267 | 268 | //刷新界面 269 | if (self.onlySlider) { 270 | [self hide]; 271 | }else{ 272 | [self show]; 273 | } 274 | } 275 | 276 | #pragma mark - Public Methods 277 | - (void)setProgress:(CGFloat)progressValue{ 278 | 279 | [self.bufferSlider setProgressValue:progressValue]; 280 | 281 | [self.playTimeLbl setText:[NSString stringWithFormat:@"%@",[HJVideoTimeUtil hmsStringWithFloat:progressValue]]]; 282 | } 283 | 284 | - (void)setBufferValue:(CGFloat)bufferValue{ 285 | 286 | [self.bufferSlider setBufferValue:bufferValue]; 287 | } 288 | 289 | - (void)setMaximumValue:(CGFloat)value{ 290 | 291 | [self.bufferSlider setMaximumValue:value]; 292 | 293 | [self.totalDurationLbl setText:[NSString stringWithFormat:@"%@",[HJVideoTimeUtil hmsStringWithFloat:value]]]; 294 | } 295 | 296 | - (void)show{ 297 | 298 | CGFloat height = self.fullScreen?kBottomBarFullHeight:kBottomBarHalfHeight; 299 | CGFloat width = CGRectGetWidth(self.superview.frame); 300 | CGFloat originY = CGRectGetHeight(self.superview.frame) - height; 301 | 302 | [UIView animateWithDuration:kDefaultAnimationDuration animations:^{ 303 | self.frame = CGRectMake(0, originY, width, height); 304 | } completion:^(BOOL finished) { 305 | [self changeGradientRect]; 306 | }]; 307 | 308 | 309 | self.onlySlider = NO; 310 | 311 | [self refreshUI]; 312 | } 313 | 314 | 315 | - (void)hide{ 316 | 317 | CGFloat width = CGRectGetWidth(self.superview.frame); 318 | CGFloat originY = CGRectGetHeight(self.superview.frame) - kSliderHeight; 319 | 320 | [UIView animateWithDuration:kDefaultAnimationDuration animations:^{ 321 | self.frame = CGRectMake(0, originY, width, kSliderHeight); 322 | self.bufferSlider.frame = self.bounds; 323 | }]; 324 | 325 | self.onlySlider = YES; 326 | 327 | [self refreshUI]; 328 | } 329 | @end 330 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoMaskView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoMaskView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/14. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NS_ENUM(NSUInteger, VideoMaskViewStatus) { 13 | VideoMaskViewStatus_hide = 0, 14 | VideoMaskViewStatus_showPlayBtn = 1, 15 | VideoMaskViewStatus_showReplayBtn = 2, 16 | VideoMaskViewStatus_showFastForward = 3, 17 | VideoMaskViewStatus_showLoading = 4, 18 | VideoMaskViewStatus_showPlayFailed = 5 19 | }; 20 | 21 | typedef void(^videoPlayerPlayBlock)(BOOL isPlay); 22 | typedef void(^videoPlayerReplayClickBlock)(void); 23 | typedef void(^videoPlayerPlayFailedClickBlock)(void); 24 | 25 | #import "HJFastForwardView.h" 26 | 27 | @interface HJVideoMaskView : UIView 28 | 29 | /** 视频播放回调*/ 30 | @property (nonatomic, copy) videoPlayerPlayBlock playBlock; 31 | /** 视频播放回调*/ 32 | @property (nonatomic, copy) videoPlayerReplayClickBlock replayBlock; 33 | /** 视频播放失败点击回调*/ 34 | @property (nonatomic, copy) videoPlayerPlayFailedClickBlock playFailedClickBlock; 35 | 36 | /** 快进视图 */ 37 | @property (nonatomic, strong, readonly) HJFastForwardView *fastForwardView; 38 | /** 是否暂停中 */ 39 | @property (nonatomic, assign, readonly) BOOL isPausing; 40 | /** 遮罩层显示状态 */ 41 | @property (nonatomic, assign) VideoMaskViewStatus maskViewStatus; 42 | 43 | 44 | - (void)setPlayStatus:(BOOL)play; 45 | 46 | - (void)show; 47 | 48 | - (void)hide; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoMaskView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoMaskView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/14. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoMaskView.h" 10 | #import "HJVideoPlayerHeader.h" 11 | #import "HJCircleLoading.h" 12 | #import "HJVideoConst.h" 13 | #import "HJViewFactory.h" 14 | 15 | @interface HJVideoMaskView () 16 | 17 | @property (nonatomic ,strong) UIButton * playBtn; 18 | 19 | @property (nonatomic ,strong) UIButton * replayBtn; 20 | // 当前显示的视图 21 | @property (nonatomic, strong) UIView *currentShowV; 22 | /** 快进视图 */ 23 | @property (nonatomic, strong) HJFastForwardView *fastForwardView; 24 | /** 加载视图 */ 25 | @property (nonatomic, strong) HJCircleLoading *circleLoading; 26 | 27 | @end 28 | 29 | 30 | 31 | 32 | @implementation HJVideoMaskView 33 | 34 | 35 | -(instancetype)initWithFrame:(CGRect)frame;{ 36 | self = [super initWithFrame:frame]; 37 | if(self){ 38 | [self setupUI]; 39 | 40 | } 41 | return self; 42 | } 43 | 44 | 45 | - (instancetype)init 46 | { 47 | self = [super init]; 48 | if (self) { 49 | [self setupUI]; 50 | } 51 | return self; 52 | } 53 | 54 | #pragma mark - About UI 55 | 56 | - (void)setupUI{ 57 | 58 | } 59 | 60 | - (void)show{ 61 | 62 | self.hidden = NO; 63 | } 64 | 65 | - (void)hide{ 66 | 67 | self.hidden = YES; 68 | } 69 | 70 | - (void)showPlayBtn{ 71 | 72 | [self showSomeView:self.playBtn]; 73 | } 74 | 75 | 76 | - (void)showReplayBtn{ 77 | 78 | [self.replayBtn removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; 79 | [self.replayBtn addTarget:self action:@selector(playFailedClickAction) forControlEvents:UIControlEventTouchUpInside]; 80 | [self.replayBtn setTitle:@"重新播放" forState:UIControlStateNormal]; 81 | [self showSomeView:self.replayBtn]; 82 | } 83 | 84 | - (void)showPlayFailed{ 85 | 86 | [self.replayBtn removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside]; 87 | [self.replayBtn addTarget:self action:@selector(replayAction) forControlEvents:UIControlEventTouchUpInside]; 88 | [self.replayBtn setTitle:@"播放失败" forState:UIControlStateNormal]; 89 | [self showSomeView:self.replayBtn]; 90 | } 91 | 92 | - (void)showFastForward{ 93 | 94 | [self showSomeView:self.fastForwardView]; 95 | } 96 | 97 | - (void)showLoading{ 98 | 99 | [self showSomeView:self.circleLoading]; 100 | [self.circleLoading startAnimating]; 101 | } 102 | 103 | - (void)showSomeView:(UIView *)showView{ 104 | 105 | self.playBtn.hidden = YES; 106 | self.replayBtn.hidden = YES; 107 | self.fastForwardView.hidden = YES; 108 | self.circleLoading.hidden = YES; 109 | [self.circleLoading stopAnimating]; 110 | 111 | self.hidden = NO; 112 | self.currentShowV = showView; 113 | 114 | showView.hidden = NO; 115 | showView.center = self.center; 116 | } 117 | 118 | 119 | #pragma mark - Event response 120 | - (void)setPlayStatus:(BOOL)play;{ 121 | 122 | self.playBtn.selected = play; 123 | 124 | } 125 | - (void)playOrPauseAction:(UIButton *)sender{ 126 | 127 | sender.selected = !sender.selected; 128 | if (self.playBlock) { 129 | self.playBlock(sender.selected); 130 | } 131 | } 132 | 133 | - (void)replayAction{ 134 | 135 | if(self.replayBlock){ 136 | self.replayBlock(); 137 | } 138 | } 139 | 140 | - (void)playFailedClickAction{ 141 | 142 | if (self.playFailedClickBlock) { 143 | self.playFailedClickBlock(); 144 | } 145 | } 146 | #pragma mark - Private methods 147 | 148 | #pragma mark - Public methods 149 | - (BOOL)isPausing{ 150 | 151 | return !self.playBtn.selected; 152 | } 153 | #pragma mark - Delegate methods 154 | 155 | #pragma mark - getters and setters 156 | - (UIButton *)playBtn 157 | { 158 | if (!_playBtn) { 159 | _playBtn = [HJViewFactory buttonWithNormalImage:imgPlay selectedImage:imgPause]; 160 | [_playBtn addTarget:self action:@selector(playOrPauseAction:) forControlEvents:UIControlEventTouchUpInside]; 161 | [_playBtn setImageEdgeInsets:UIEdgeInsetsZero]; 162 | [_playBtn setFrame:CGRectMake(0, 0, 60, 60)]; 163 | [_playBtn setHidden:YES]; 164 | [self addSubview:_playBtn]; 165 | } 166 | return _playBtn; 167 | } 168 | 169 | 170 | - (UIButton *)replayBtn 171 | { 172 | if (!_replayBtn) { 173 | _replayBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 174 | [_replayBtn setFrame:CGRectMake(0, 0, 100, 50)]; 175 | [_replayBtn setHidden:YES]; 176 | [_replayBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 177 | [_replayBtn setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.6]]; 178 | _replayBtn.titleLabel.font = [UIFont systemFontOfSize:16]; 179 | _replayBtn.layer.cornerRadius = 4.f; 180 | _replayBtn.layer.masksToBounds = YES; 181 | [self addSubview:_replayBtn]; 182 | } 183 | return _replayBtn; 184 | } 185 | 186 | - (HJFastForwardView *)fastForwardView{ 187 | if (!_fastForwardView) { 188 | _fastForwardView = [[HJFastForwardView alloc] init]; 189 | [_fastForwardView setFrame:CGRectMake(0, 0, 150, 100)]; 190 | [_fastForwardView setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.7]]; 191 | [_fastForwardView setHidden:YES]; 192 | [_fastForwardView configForwardLeftImage:[UIImage imageNamed:@"video_farword_left"] forwardRightImage:[UIImage imageNamed:@"video_farword_right"]]; 193 | _fastForwardView.layer.cornerRadius = 8.f; 194 | _fastForwardView.layer.masksToBounds = YES; 195 | [self addSubview:_fastForwardView]; 196 | } 197 | return _fastForwardView; 198 | } 199 | 200 | 201 | - (HJCircleLoading *)circleLoading{ 202 | if (!_circleLoading) { 203 | _circleLoading = [[HJCircleLoading alloc] init]; 204 | _circleLoading.frame = CGRectMake(0, 0, 80, 80); 205 | _circleLoading.backgroundColor = [UIColor clearColor]; 206 | _circleLoading.hidden = YES; 207 | [self addSubview:_circleLoading]; 208 | } 209 | return _circleLoading; 210 | } 211 | 212 | - (void)setMaskViewStatus:(VideoMaskViewStatus)maskViewStatus 213 | { 214 | _maskViewStatus = maskViewStatus; 215 | 216 | switch (maskViewStatus) { 217 | case VideoMaskViewStatus_hide: 218 | [self hide]; 219 | break; 220 | case VideoMaskViewStatus_showPlayBtn: 221 | [self showPlayBtn]; 222 | break; 223 | case VideoMaskViewStatus_showReplayBtn: 224 | [self showReplayBtn]; 225 | break; 226 | case VideoMaskViewStatus_showPlayFailed: 227 | [self showPlayFailed]; 228 | break; 229 | case VideoMaskViewStatus_showFastForward: 230 | [self showFastForward]; 231 | break; 232 | case VideoMaskViewStatus_showLoading: 233 | [self showLoading]; 234 | break; 235 | default: 236 | 237 | break; 238 | } 239 | } 240 | 241 | - (void)setFrame:(CGRect)frame{ 242 | [super setFrame:frame]; 243 | 244 | if (_currentShowV) { 245 | self.currentShowV.center = self.center; 246 | } 247 | } 248 | 249 | @end 250 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoPlayerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerController.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2016/12/8. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HJVideoConfigModel.h" 11 | 12 | typedef NS_ENUM(NSUInteger, VideoPlayerStatus) { 13 | videoPlayer_unknown, 14 | videoPlayer_readyToPlay, 15 | videoPlayer_playing, 16 | videoPlayer_pause, 17 | videoPlayer_loading, 18 | videoPlayer_playEnd, 19 | videoPlayer_playFailed 20 | }; 21 | 22 | 23 | typedef void (^VideoPlayerControllerBackBtnClick)(void); 24 | typedef void (^VideoPlayerControllerPlayEndBlock)(void) ; 25 | typedef void (^VideoPlayerChangeScreenBtnClick)(BOOL changeFull); 26 | 27 | 28 | @class HJPlayerView; 29 | @interface HJVideoPlayerController : UIViewController 30 | 31 | @property (nonatomic ,copy) NSString *url; 32 | /** 视频标题 */ 33 | @property (nonatomic, copy) NSString *videoTitle; 34 | /** 是否全屏状态 */ 35 | @property (nonatomic, assign , readonly) BOOL isFullScreen; 36 | /** 播放状态 */ 37 | @property (nonatomic, assign , readonly) VideoPlayerStatus playStatus; 38 | /** 用于设置第一帧图片 */ 39 | @property (nonatomic, strong , readonly) HJPlayerView *playerView; 40 | /** 配置模型 */ 41 | @property (nonatomic, strong) HJVideoConfigModel *configModel; 42 | /** 返回按钮点击回调 */ 43 | @property (nonatomic, copy) VideoPlayerControllerBackBtnClick backBlock; 44 | /** 播放完成回调 */ 45 | @property (nonatomic, copy) VideoPlayerControllerPlayEndBlock playEndBlock; 46 | /** 屏幕切换回调 */ 47 | @property (nonatomic, copy) VideoPlayerChangeScreenBtnClick screenChangedBlock; 48 | 49 | 50 | /** 播放 */ 51 | - (void)play; 52 | 53 | /** 暂停 */ 54 | - (void)pause; 55 | 56 | /** 暂停播放和缓冲 */ 57 | - (void)pausePlayAndBuffer; 58 | 59 | - (instancetype)initWithFrame:(CGRect)frame; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoPlayerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerController.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2016/12/8. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoPlayerController.h" 10 | #import "HJVideoTopView.h" 11 | #import "HJVideoBottomView.h" 12 | #import "HJVideoPlayManager.h" 13 | #import "HJVideoUIManager.h" 14 | #import "HJPlayerView.h" 15 | #import 16 | #import "HJViewFactory.h" 17 | #import "HJVideoMaskView.h" 18 | #import "HJVideoPlayerHeader.h" 19 | #import "AppDelegate+HJVideoRotation.h" 20 | #import "HJVideoPlayerUtil.h" 21 | #import "UIDevice+HJVideoRotation.h" 22 | #import "HJVideoConst.h" 23 | #import "HJVideoPlayTimeRecord.h" 24 | 25 | typedef NS_ENUM(NSUInteger, MoveDirection) { 26 | MoveDirection_none = 0, 27 | MoveDirection_up, 28 | MoveDirection_down, 29 | MoveDirection_left, 30 | MoveDirection_right 31 | }; 32 | 33 | 34 | 35 | @interface HJVideoPlayerController () 36 | 37 | @property (nonatomic ,strong) HJVideoMaskView * maskView; 38 | 39 | @property (nonatomic ,strong) HJVideoTopView * topView; 40 | 41 | @property (nonatomic ,strong) HJVideoBottomView * bottomView; 42 | 43 | @property (nonatomic ,assign) CGRect originFrame; 44 | 45 | @property (nonatomic, assign) BOOL isFullScreen; 46 | 47 | @property (nonatomic, strong) HJPlayerView *playerView; 48 | 49 | @property (nonatomic, assign) VideoPlayerStatus playStatus; 50 | 51 | @property (nonatomic, assign) VideoPlayerStatus prePlayStatus; 52 | /** 隐藏时间 */ 53 | @property (nonatomic, assign) NSInteger secondsForBottom; 54 | /** 开始移动 */ 55 | @property (nonatomic, assign) CGPoint startPoint; 56 | /** 移动方向 */ 57 | @property (nonatomic, assign) MoveDirection moveDirection; 58 | /** 音量调节 */ 59 | @property (nonatomic, strong) UISlider *volumeSlider; 60 | /** 系统音量 */ 61 | @property (nonatomic, assign) CGFloat sysVolume; 62 | /** 亮度调节 */ 63 | @property (nonatomic, assign) CGFloat brightness; 64 | /** 进度调节 */ 65 | @property (nonatomic, assign) CGFloat currentTime; 66 | /** 定时器 */ 67 | @property (nonatomic, strong) NSTimer *timer; 68 | 69 | 70 | 71 | @end 72 | 73 | #define kFullScreenFrame CGRectMake(0 , 0, kVideoScreenH, kVideoScreenW) 74 | 75 | 76 | static const NSInteger maxSecondsForBottom = 5.f; 77 | 78 | @implementation HJVideoPlayerController 79 | 80 | #pragma mark - 生命周期 81 | 82 | - (instancetype)initWithFrame:(CGRect)frame{ 83 | self = [super init]; 84 | if (self) { 85 | self.originFrame = frame; 86 | } 87 | return self; 88 | } 89 | 90 | - (void)viewDidLoad { 91 | [super viewDidLoad]; 92 | [self setupUI]; 93 | [self initW]; 94 | [self handleVideoPlayerStatus]; 95 | [self handleProgress]; 96 | [self addObservers]; 97 | [self addTapGesture]; 98 | } 99 | 100 | - (void)didReceiveMemoryWarning { 101 | [super didReceiveMemoryWarning]; 102 | } 103 | 104 | - (void)viewWillAppear:(BOOL)animated{ 105 | [super viewWillAppear:animated]; 106 | [self.navigationController setNavigationBarHidden:YES animated:NO]; 107 | //允许屏幕旋转 108 | AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 109 | if (self.configModel.onlyFullScreen) { 110 | delegate.orientationMask = UIInterfaceOrientationMaskLandscape; 111 | [UIDevice rotateToOrientation:UIInterfaceOrientationLandscapeRight]; 112 | [self changeFullScreen:YES]; 113 | }else{ 114 | delegate.orientationMask = UIInterfaceOrientationMaskAllButUpsideDown; 115 | } 116 | //禁用侧滑手势方法 117 | self.navigationController.interactivePopGestureRecognizer.enabled = NO; 118 | //开启屏幕长亮 119 | [UIApplication sharedApplication].idleTimerDisabled = YES; 120 | } 121 | 122 | - (void)viewWillDisappear:(BOOL)animated{ 123 | [super viewWillDisappear:animated]; 124 | [self.navigationController setNavigationBarHidden:NO animated:NO]; 125 | [self cancelTimer]; 126 | [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:NO]; 127 | //关闭屏幕旋转 128 | AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 129 | delegate.orientationMask = UIInterfaceOrientationMaskPortrait; 130 | [UIDevice rotateToOrientation:UIInterfaceOrientationPortrait]; 131 | //关闭屏幕长亮 132 | [UIApplication sharedApplication].idleTimerDisabled = NO; 133 | //禁用侧滑手势方法 134 | self.navigationController.interactivePopGestureRecognizer.enabled = YES; 135 | //暂停播放 136 | [self pause]; 137 | } 138 | 139 | 140 | - (void)dealloc{ 141 | 142 | NSLog(@"播放器已销毁"); 143 | [kVideoPlayerManager reset]; 144 | } 145 | 146 | 147 | 148 | - (void)initW 149 | { 150 | self.secondsForBottom = maxSecondsForBottom; 151 | self.currentTime = 0; 152 | 153 | //获取系统音量滚动条 154 | MPVolumeView *volumeView = [[MPVolumeView alloc]init]; 155 | for (UIView *tmpView in volumeView.subviews) { 156 | if ([[tmpView.class description] isEqualToString:@"MPVolumeSlider"]) { 157 | self.volumeSlider = (UISlider *)tmpView; 158 | } 159 | } 160 | } 161 | 162 | 163 | - (void)clearInfo{ 164 | 165 | if (_bottomView) { 166 | [self.bottomView setProgress:0]; 167 | [self.bottomView setMaximumValue:0]; 168 | [self.bottomView setBufferValue:0]; 169 | } 170 | 171 | self.playStatus = videoPlayer_unknown; 172 | self.currentTime = 0; 173 | } 174 | #pragma mark - About UI 175 | - (void)setupUI 176 | { 177 | // 设置self 178 | if (!self.configModel.onlyFullScreen) { 179 | [self.view setFrame:self.originFrame]; 180 | }else{ 181 | [self.view setFrame:CGRectMake(0, 0,kVideoScreenW, kVideoScreenH)]; 182 | } 183 | 184 | [self.view setClipsToBounds:YES]; 185 | 186 | // 设置player 187 | [self.playerView setFrame:self.view.frame]; 188 | [self.playerView setBackgroundColor:[UIColor clearColor]]; 189 | [self.playerView setImage:imgVideoBackImg]; 190 | [self.playerView setUserInteractionEnabled:YES]; 191 | [self.view addSubview:self.playerView]; 192 | 193 | 194 | //设置遮罩层 195 | self.maskView = [[HJVideoMaskView alloc] initWithFrame:self.playerView.bounds]; 196 | self.maskView.backgroundColor = kVideoMaskViewBGColor; 197 | self.maskView.maskViewStatus = VideoMaskViewStatus_showPlayBtn; 198 | WS(weakSelf); 199 | self.maskView.playBlock = ^(BOOL isPlay) { 200 | if (isPlay) { 201 | [weakSelf play]; 202 | }else{ 203 | [weakSelf pause]; 204 | } 205 | 206 | [weakSelf resetTimer]; 207 | }; 208 | 209 | __weak HJVideoMaskView * weakMaskView = self.maskView; 210 | self.maskView.replayBlock = ^{ 211 | [kVideoPlayerManager seekToTime:0]; 212 | weakMaskView.maskViewStatus = VideoMaskViewStatus_showPlayBtn; 213 | [weakSelf resetTimer]; 214 | }; 215 | 216 | self.maskView.playFailedClickBlock = ^{ 217 | [weakSelf setUrl:weakSelf.url]; 218 | }; 219 | [self.playerView addSubview:self.maskView]; 220 | 221 | 222 | 223 | // 设置topView 224 | self.topView.frame = CGRectMake(0, 0, self.view.frame.size.width, kBottomBarHalfHeight); 225 | [self.view addSubview:self.topView]; 226 | 227 | // 设置BottomView 228 | self.bottomView.frame = CGRectMake(0, CGRectGetHeight(self.view.frame) - kBottomBarHalfHeight, self.view.frame.size.width, kBottomBarHalfHeight); 229 | self.bottomView.configModel = self.configModel; 230 | [self.view addSubview:self.bottomView]; 231 | } 232 | 233 | 234 | - (void)changeFullScreen:(BOOL)changeFull{ 235 | 236 | self.isFullScreen = changeFull; 237 | 238 | [self.view.superview bringSubviewToFront:self.view]; 239 | 240 | [UIView animateWithDuration:kDefaultAnimationDuration animations:^{ 241 | if (changeFull) { 242 | self.view.frame = kVideoFullFrame; 243 | }else{ 244 | self.view.frame = self.originFrame; 245 | } 246 | 247 | self.playerView.frame = self.view.bounds; 248 | self.maskView.frame = self.playerView.bounds; 249 | 250 | // 发送屏幕改变通知 251 | [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationChangeScreen object:@(changeFull)]; 252 | }]; 253 | } 254 | #pragma mark - 底部栏显示/隐藏 255 | 256 | - (void)addTapGesture 257 | { 258 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(startTimer)]; 259 | [self.view addGestureRecognizer:tap]; 260 | [self startTimer]; 261 | } 262 | 263 | - (void)showBottomAction 264 | { 265 | if (!self.bottomView.onlySlider) { 266 | [self.bottomView hide]; 267 | }else{ 268 | [self.bottomView show]; 269 | } 270 | } 271 | 272 | 273 | - (void)startTimer 274 | { 275 | //看是否正在计时,若是则结束计时 否则开始计时 276 | if (self.timer) { 277 | 278 | [self cancelTimerAndHideViews]; 279 | 280 | }else{ 281 | [self setSecondsForBottom:maxSecondsForBottom]; 282 | [self hideTopView:NO]; 283 | [self.bottomView show]; 284 | 285 | [self.maskView show]; 286 | self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self 287 | selector:@selector(timerAction) 288 | userInfo:nil 289 | repeats:YES]; 290 | } 291 | } 292 | 293 | 294 | - (void)resetTimer{ 295 | 296 | if ([self.timer isValid]) { 297 | self.secondsForBottom = 5.f; 298 | }else{ 299 | [self startTimer]; 300 | } 301 | } 302 | 303 | 304 | - (void)cancelTimerAndHideViews{ 305 | 306 | [self hideSomeViews]; 307 | 308 | [self cancelTimer]; 309 | } 310 | 311 | 312 | - (void)cancelTimer{ 313 | 314 | 315 | if (!self.timer) { 316 | return; 317 | } 318 | 319 | [self.timer invalidate]; 320 | self.timer = nil; 321 | } 322 | 323 | - (void)hideSomeViews{ 324 | 325 | //顶部视图隐藏 326 | if (self.isFullScreen) { 327 | [self hideTopView:YES]; 328 | } 329 | 330 | //底部视图隐藏 331 | [self.bottomView hide]; 332 | 333 | //中间内容隐藏 暂停 播放失败 和播放结束 无需隐藏显示内容 334 | if(self.playStatus != videoPlayer_readyToPlay && 335 | self.maskView.maskViewStatus != VideoMaskViewStatus_showLoading && 336 | self.maskView.maskViewStatus != VideoMaskViewStatus_showPlayFailed && 337 | !self.maskView.isPausing){ 338 | [self.maskView hide]; 339 | } 340 | 341 | self.secondsForBottom = 0; 342 | } 343 | 344 | 345 | - (void)hideTopView:(BOOL)hide{ 346 | 347 | self.topView.hidden = hide; 348 | //状态栏显示跟随topView 349 | [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:NO]; 350 | } 351 | 352 | 353 | - (void)timerAction{ 354 | 355 | self.secondsForBottom --; 356 | NSLog(@"隐藏底部栏:%zd",self.secondsForBottom); 357 | if (self.secondsForBottom <= 0) { 358 | [self cancelTimerAndHideViews]; 359 | } 360 | } 361 | 362 | 363 | #pragma mark - Pravite Methods 364 | - (void)handleVideoPlayerStatus{ 365 | 366 | WS(weakSelf); 367 | [kVideoPlayerManager readyBlock:^(CGFloat totoalDuration) { 368 | NSLog(@"[%@]:准备播放",[weakSelf class]); 369 | weakSelf.playStatus = videoPlayer_readyToPlay; 370 | if (weakSelf.configModel.autoPlay) {//是否自动播放 371 | [weakSelf play]; 372 | }else{ 373 | weakSelf.maskView.maskViewStatus = VideoMaskViewStatus_showPlayBtn; 374 | } 375 | } monitoringBlock:^(CGFloat currentDuration) { 376 | 377 | if(weakSelf.playStatus!= videoPlayer_pause){ 378 | weakSelf.playStatus = videoPlayer_playing; 379 | } 380 | 381 | if(weakSelf.maskView.maskViewStatus != VideoMaskViewStatus_showPlayBtn) { 382 | weakSelf.maskView.maskViewStatus = VideoMaskViewStatus_showPlayBtn; 383 | [weakSelf hideSomeViews]; 384 | }; 385 | }loadingBlock:^{ 386 | NSLog(@"[%@]:加载中",[weakSelf class]); 387 | weakSelf.playStatus = videoPlayer_loading; 388 | weakSelf.maskView.maskViewStatus = VideoMaskViewStatus_showLoading; 389 | }endBlock:^{ 390 | NSLog(@"[%@]:播放结束",[weakSelf class]); 391 | weakSelf.playStatus = videoPlayer_playEnd; 392 | weakSelf.maskView.maskViewStatus = VideoMaskViewStatus_showReplayBtn; 393 | if (weakSelf.playEndBlock) { 394 | weakSelf.playEndBlock(); 395 | } 396 | } failedBlock:^{ 397 | NSLog(@"[%@]:播放失败",[weakSelf class]); 398 | weakSelf.playStatus = videoPlayer_playFailed; 399 | weakSelf.maskView.maskViewStatus = VideoMaskViewStatus_showPlayFailed; 400 | }]; 401 | } 402 | /** 403 | * 设置时长 404 | */ 405 | - (void)handleProgress{ 406 | 407 | WS(weakSelf); 408 | [kVideoPlayerManager totalDurationBlock:^(CGFloat totalDuration) { 409 | [weakSelf.bottomView setMaximumValue:totalDuration]; 410 | } currentDurationBlock:^(CGFloat currentDuration) { 411 | [weakSelf.bottomView setProgress:currentDuration]; 412 | } bufferDurationBlock:^(CGFloat bufferDuration) { 413 | [weakSelf.bottomView setBufferValue:bufferDuration]; 414 | }]; 415 | } 416 | 417 | - (void)addObservers{ 418 | 419 | [[NSNotificationCenter defaultCenter] addObserver:self 420 | selector:@selector(applicationDidEnterBackground) 421 | name:UIApplicationDidEnterBackgroundNotification 422 | object:nil]; 423 | 424 | [[NSNotificationCenter defaultCenter] addObserver:self 425 | selector:@selector(applicationWillEnterForeground) 426 | name:UIApplicationWillEnterForegroundNotification 427 | object:nil]; 428 | 429 | } 430 | 431 | 432 | #pragma mark - 公开方法 433 | - (void)play{ 434 | 435 | //若有记录则从记录播放 436 | if ([kVideoPlayerManager recordWithUrl:self.url]) { 437 | HJVideoPlayTimeRecord * record = [kVideoPlayerManager recordWithUrl:self.url]; 438 | [kVideoPlayerManager seekToTime:record.playTime]; 439 | [kVideoPlayerManager removeRecord:record]; 440 | } 441 | 442 | [kVideoPlayerManager play]; 443 | [self setPlayStatus:videoPlayer_playing]; 444 | [self.maskView setPlayStatus:YES]; 445 | } 446 | 447 | - (void)pause{ 448 | 449 | [kVideoPlayerManager pause]; 450 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 451 | [self setPlayStatus:videoPlayer_pause]; 452 | [self.maskView setPlayStatus:NO]; 453 | }); 454 | } 455 | 456 | 457 | - (void)pausePlayAndBuffer{ 458 | 459 | [self pause]; 460 | 461 | [kVideoPlayerManager cancelBuffer]; 462 | } 463 | 464 | 465 | #pragma mark - 事件响应 466 | - (void)applicationDidEnterBackground { 467 | 468 | if (self.playStatus == videoPlayer_playing) { 469 | [self pause]; 470 | } 471 | } 472 | 473 | 474 | - (void)applicationWillEnterForeground { 475 | 476 | if (self.prePlayStatus == videoPlayer_playing) { 477 | [self play]; 478 | } 479 | } 480 | 481 | - (void)playOrPauseAction:(UIButton *)sender{ 482 | sender.selected = !sender.selected; 483 | if (sender.selected) { 484 | [self play]; 485 | }else{ 486 | [self pause]; 487 | } 488 | } 489 | 490 | 491 | - (void)popAction{ 492 | [self.navigationController popViewControllerAnimated:YES]; 493 | [self.view removeFromSuperview]; 494 | [self removeFromParentViewController]; 495 | } 496 | #pragma mark - getters / setters 497 | - (HJVideoTopView *)topView{ 498 | if (!_topView) { 499 | WS(weakSelf); 500 | _topView = [[HJVideoTopView alloc]init]; 501 | _topView.backBlock = ^(){ 502 | if (weakSelf.configModel.onlyFullScreen) {//仅支持全屏 直接返回 503 | weakSelf.view.frame = CGRectZero; 504 | [weakSelf.navigationController popViewControllerAnimated:YES]; 505 | }else{ 506 | if(weakSelf.isFullScreen){//全屏返回 507 | [UIDevice rotateToOrientation:UIInterfaceOrientationPortrait]; 508 | [weakSelf changeFullScreen:NO]; 509 | }else{//半屏返回操作 510 | [weakSelf popAction]; 511 | } 512 | } 513 | }; 514 | 515 | _topView.showListBlock = ^(BOOL show){ 516 | 517 | }; 518 | } 519 | return _topView; 520 | } 521 | 522 | - (HJVideoBottomView *)bottomView{ 523 | if (!_bottomView) { 524 | WS(weakSelf); 525 | _bottomView = [[HJVideoBottomView alloc] init]; 526 | _bottomView.fullScreenBlock = ^(BOOL isFull){ 527 | if(isFull){ 528 | [UIDevice rotateToOrientation:UIInterfaceOrientationLandscapeRight]; 529 | }else{ 530 | [UIDevice rotateToOrientation:UIInterfaceOrientationPortrait]; 531 | } 532 | [weakSelf changeFullScreen:isFull]; 533 | }; 534 | 535 | _bottomView.valueChangedBlock = ^(float value) { 536 | [kVideoPlayerManager seekToTime:value]; 537 | }; 538 | } 539 | return _bottomView; 540 | } 541 | 542 | - (HJPlayerView *)playerView{ 543 | if (!_playerView) { 544 | _playerView = [[HJPlayerView alloc] init]; 545 | } 546 | return _playerView; 547 | } 548 | 549 | 550 | - (void)setUrl:(NSString *)url{ 551 | if (!url) return; 552 | 553 | if (![_url isEqualToString:url]) {//若切换URL 554 | //清除视频标题 555 | self.videoTitle = @""; 556 | } 557 | 558 | //记录播放时长 559 | [kVideoPlayerManager recordUrl:_url playTime:self.bottomView.progressValue]; 560 | 561 | _url = url; 562 | 563 | [self clearInfo]; 564 | [self.playerView setPlayer:[kVideoPlayerManager setUrl:_url]]; 565 | self.maskView.maskViewStatus = VideoMaskViewStatus_showLoading; 566 | } 567 | 568 | - (void)setVideoTitle:(NSString *)videoTitle{ 569 | _videoTitle = videoTitle; 570 | self.topView.title = videoTitle; 571 | } 572 | 573 | - (void)setPlayStatus:(VideoPlayerStatus)playStatus{ 574 | 575 | self.prePlayStatus = _playStatus; 576 | _playStatus = playStatus; 577 | } 578 | 579 | - (HJVideoConfigModel *)configModel{ 580 | 581 | if (!_configModel) { 582 | _configModel = [[HJVideoConfigModel alloc] init]; 583 | _configModel.autoPlay = YES;//默认设置url自动播放 584 | } 585 | return _configModel; 586 | } 587 | 588 | 589 | - (void)setIsFullScreen:(BOOL)isFullScreen{ 590 | 591 | if (_isFullScreen != isFullScreen) { 592 | _isFullScreen = isFullScreen; 593 | //屏幕切换回调 594 | if (self.screenChangedBlock) { 595 | self.screenChangedBlock(isFullScreen); 596 | } 597 | } 598 | } 599 | #pragma mark - 触摸事件 600 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 601 | 602 | [super touchesBegan:touches withEvent:event]; 603 | UITouch * touch = [touches anyObject]; 604 | self.startPoint = [touch locationInView:self.view]; 605 | self.sysVolume = self.volumeSlider.value; 606 | self.brightness = [UIScreen mainScreen].brightness; 607 | self.currentTime = self.bottomView.progressValue; 608 | } 609 | 610 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 611 | 612 | [super touchesMoved:touches withEvent:event]; 613 | 614 | //状态未知时不可拖动 如(未进入准备状态) 615 | if(self.playStatus == videoPlayer_unknown || 616 | self.playStatus == videoPlayer_playFailed || 617 | self.playStatus == videoPlayer_playEnd || 618 | self.playStatus == videoPlayer_loading){ 619 | return; 620 | } 621 | 622 | [self setSecondsForBottom:maxSecondsForBottom]; 623 | UITouch * touch = [touches anyObject]; 624 | CGPoint movePoint = [touch locationInView:self.view]; 625 | 626 | CGFloat subX = movePoint.x - self.startPoint.x; 627 | CGFloat subY = movePoint.y - self.startPoint.y; 628 | CGFloat width = self.view.frame.size.width; 629 | CGFloat height = self.view.frame.size.height; 630 | 631 | BOOL startInLeft = movePoint.x < width/2.f; 632 | 633 | 634 | if (self.moveDirection == MoveDirection_none) { 635 | if (subX >= 30) { 636 | self.moveDirection = MoveDirection_right; 637 | }else if(subX <= -30){ 638 | self.moveDirection = MoveDirection_left; 639 | }else if (subY >= 30){ 640 | self.moveDirection = MoveDirection_down; 641 | }else if (subY <= -30){ 642 | self.moveDirection = MoveDirection_up; 643 | } 644 | } 645 | 646 | if (self.moveDirection == MoveDirection_right || self.moveDirection == MoveDirection_left) {//快进 647 | CGFloat ratio = subX/width; 648 | CGFloat offsetSeconds = self.bottomView.maximumValue*ratio; 649 | CGFloat seekTime = self.currentTime + offsetSeconds; 650 | [self.maskView.fastForwardView setMaxDuration:self.bottomView.maximumValue]; 651 | self.maskView.maskViewStatus = VideoMaskViewStatus_showFastForward; 652 | [self.maskView.fastForwardView moveRight:offsetSeconds>0]; 653 | [self.maskView.fastForwardView setProgress:seekTime/self.bottomView.maximumValue]; 654 | [self resetTimer]; 655 | [self.bottomView hide]; 656 | }else if (self.moveDirection == MoveDirection_up || self.moveDirection == MoveDirection_down){ 657 | if (startInLeft) {//上调亮度 658 | [UIScreen mainScreen].brightness = self.brightness - subY/height;//10; 659 | }else{//上调音量 660 | self.volumeSlider.value = self.sysVolume - subY/height;//10; 661 | } 662 | } 663 | } 664 | 665 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 666 | 667 | [super touchesEnded:touches withEvent:event]; 668 | if (self.moveDirection == MoveDirection_left || self.moveDirection == MoveDirection_right) { 669 | [kVideoPlayerManager seekToTime:self.maskView.fastForwardView.currentDuration]; 670 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 671 | self.playStatus = self.prePlayStatus; 672 | [self cancelTimerAndHideViews]; 673 | }); 674 | } 675 | [self setMoveDirection:MoveDirection_none]; 676 | [self setCurrentTime:0]; 677 | } 678 | 679 | 680 | #pragma mark - 屏幕旋转 681 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 682 | { 683 | //全屏不支持屏幕旋转 684 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 685 | if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { 686 | //屏幕从竖屏变为横屏时执行 687 | [self changeFullScreen:YES]; 688 | }else{ 689 | //屏幕从横屏变为竖屏时执行 690 | [self changeFullScreen:NO]; 691 | } 692 | }); 693 | } 694 | 695 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 696 | { 697 | 698 | 699 | } 700 | 701 | @end 702 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoSettingView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoSettingView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/29. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoSettingView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoSettingView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoSettingView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 2018/1/29. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoSettingView.h" 10 | 11 | @implementation HJVideoSettingView 12 | 13 | 14 | 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoTopView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoTopView.h 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^videoBackBlock)(void); 12 | 13 | typedef void(^videoShowListBlock)(BOOL show); 14 | 15 | @class HJVideoConfigModel; 16 | 17 | @interface HJVideoTopView : UIView 18 | 19 | @property (nonatomic, copy) videoBackBlock backBlock; 20 | 21 | @property (nonatomic, copy) videoShowListBlock showListBlock; 22 | 23 | @property (nonatomic, strong) HJVideoConfigModel *configModel; 24 | 25 | @property (nonatomic, copy) NSString *title; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /HJVideoPlayer/VideoClass/Views/HJVideoTopView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoTopView.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. All rights reserved. 7 | // 8 | 9 | #import "HJVideoTopView.h" 10 | #import "HJVideoPlayerHeader.h" 11 | #import "HJVideoConfigModel.h" 12 | #import "HJVideoConst.h" 13 | #import "HJViewFactory.h" 14 | 15 | @interface HJVideoTopView () 16 | 17 | @property (nonatomic ,strong) UIButton * backBtn; 18 | 19 | @property (nonatomic ,strong) UIButton * listBtn; 20 | 21 | @property (nonatomic, strong) UILabel * titleLabel; 22 | /** 渐变layer */ 23 | @property (nonatomic, strong) CAGradientLayer* gradientLayer; 24 | /** 是否全屏 */ 25 | @property (nonatomic, assign) BOOL fullScreen; 26 | 27 | @end 28 | 29 | 30 | 31 | @implementation HJVideoTopView 32 | 33 | -(instancetype)initWithFrame:(CGRect)frame;{ 34 | self = [super initWithFrame:frame]; 35 | if(self){ 36 | [self addObservers]; 37 | 38 | [self setupUI]; 39 | } 40 | return self; 41 | } 42 | 43 | - (instancetype)init 44 | { 45 | self = [super init]; 46 | if (self) { 47 | [self addObservers]; 48 | 49 | [self setupUI]; 50 | } 51 | return self; 52 | } 53 | 54 | - (void)setupUI 55 | { 56 | [self.layer addSublayer:self.gradientLayer]; 57 | 58 | [self addSubview:self.backBtn]; 59 | 60 | [self addSubview:self.listBtn]; 61 | } 62 | 63 | 64 | - (void)changeGradientWithRect:(CGRect)rect{ 65 | 66 | self.gradientLayer.frame = rect; 67 | //设置渐变区域的起始和终止位置(范围为0-1) 68 | self.gradientLayer.startPoint = CGPointMake(0, 0); 69 | self.gradientLayer.endPoint = CGPointMake(0, 1); 70 | //设置颜色数组 71 | self.gradientLayer.colors = @[(__bridge id)kVideoBottomGradientColor.CGColor, 72 | (__bridge id)[UIColor clearColor].CGColor]; 73 | //设置颜色分割点(范围:0-1) 74 | self.gradientLayer.locations = @[@(0.f), @(1.0f)]; 75 | } 76 | 77 | 78 | - (void)addObservers{ 79 | 80 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeFullScreenAction:) name:kNotificationChangeScreen object:nil]; 81 | 82 | } 83 | 84 | #pragma mark - getters / setters 85 | - (UIButton *)backBtn 86 | { 87 | if (!_backBtn) { 88 | _backBtn = [HJViewFactory buttonWithNormalImage:imgBack selectedImage:nil]; 89 | [_backBtn addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside]; 90 | [_backBtn setImageEdgeInsets:UIEdgeInsetsMake(4, 4, 4, 4)]; 91 | [_backBtn setShowsTouchWhenHighlighted:NO]; 92 | } 93 | return _backBtn; 94 | 95 | } 96 | 97 | 98 | - (UIButton *)listBtn{ 99 | if (!_listBtn) { 100 | _listBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 101 | [_listBtn setTitle:@"···" forState:UIControlStateNormal]; 102 | [_listBtn setShowsTouchWhenHighlighted:NO]; 103 | [_listBtn addTarget:self action:@selector(showOrCloseListAction:) forControlEvents:UIControlEventTouchUpInside]; 104 | [_listBtn setHidden:YES]; 105 | } 106 | return _listBtn; 107 | } 108 | 109 | 110 | - (UILabel *)titleLabel{ 111 | if (!_titleLabel) { 112 | _titleLabel = [[UILabel alloc] init]; 113 | _titleLabel.font = [UIFont systemFontOfSize:14]; 114 | _titleLabel.textColor = [UIColor whiteColor]; 115 | _titleLabel.hidden = YES; 116 | _titleLabel.numberOfLines = 1; 117 | [self addSubview:_titleLabel]; 118 | } 119 | return _titleLabel; 120 | } 121 | 122 | - (CAGradientLayer *)gradientLayer{ 123 | if (!_gradientLayer) { 124 | _gradientLayer = [[CAGradientLayer alloc] init]; 125 | } 126 | return _gradientLayer; 127 | } 128 | 129 | 130 | 131 | 132 | - (void)setFrame:(CGRect)frame{ 133 | [super setFrame:frame]; 134 | 135 | CGFloat width = frame.size.width; 136 | CGFloat height = frame.size.height; 137 | 138 | self.backBtn.frame = CGRectMake(0, 0, height, height); 139 | 140 | self.listBtn.frame = CGRectMake(width-height, 0, height, height); 141 | 142 | self.titleLabel.frame = CGRectMake(CGRectGetMaxX(self.backBtn.frame)+10, 0, width-CGRectGetMaxX(self.backBtn.frame)-self.listBtn.frame.origin.y-20, height); 143 | } 144 | 145 | - (void)setTitle:(NSString *)title{ 146 | 147 | _title = title; 148 | 149 | self.titleLabel.text = title; 150 | } 151 | 152 | #pragma mark - Event Response 153 | - (void)backAction:(UIButton *)sender 154 | { 155 | if (self.backBlock) { 156 | self.backBlock(); 157 | } 158 | } 159 | 160 | 161 | - (void)showOrCloseListAction:(UIButton *)sender 162 | { 163 | sender.selected = !sender.selected; 164 | if (self.showListBlock) { 165 | self.showListBlock(sender.selected); 166 | } 167 | } 168 | 169 | 170 | - (void)changeFullScreenAction:(NSNotification *)notif{ 171 | 172 | BOOL isFullScreen = [[notif object] boolValue]; 173 | 174 | [self setFullScreen:isFullScreen]; 175 | } 176 | 177 | 178 | - (void)setFullScreen:(BOOL)fullScreen{ 179 | 180 | _fullScreen = fullScreen; 181 | // 小屏隐藏标题 182 | self.titleLabel.hidden = !fullScreen; 183 | 184 | // 调整frame 185 | CGFloat height = fullScreen?kTopBarFullHeight:kTopBarHalfHeight; 186 | self.frame = CGRectMake(0, 0, CGRectGetWidth(self.superview.frame), height); 187 | 188 | // 调整渐变范围 189 | CGRect rect = fullScreen?self.bounds:CGRectMake(0, 0, self.bounds.size.width, 0); 190 | [self changeGradientWithRect:rect]; 191 | 192 | } 193 | 194 | @end 195 | -------------------------------------------------------------------------------- /HJVideoPlayer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HJVideoPlayer 4 | // 5 | // Created by WHJ on 16/10/17. 6 | // Copyright © 2016年 WHJ. 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 | -------------------------------------------------------------------------------- /HJVideoPlayerTests/HJVideoPlayerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerTests.m 3 | // HJVideoPlayerTests 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoPlayerTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HJVideoPlayerTests 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 | -------------------------------------------------------------------------------- /HJVideoPlayerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /HJVideoPlayerUITests/HJVideoPlayerUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HJVideoPlayerUITests.m 3 | // HJVideoPlayerUITests 4 | // 5 | // Created by WHJ on 2018/1/15. 6 | // Copyright © 2018年 WHJ. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HJVideoPlayerUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HJVideoPlayerUITests 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 | -------------------------------------------------------------------------------- /HJVideoPlayerUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 WuHuijian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HJVideoPlayer --------------------------------------------------------------------------------