├── .clang-format ├── .gitignore ├── .travis.yml ├── Example ├── LMMediaPlayer.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── LMMediaPlayer.xcscheme ├── LMMediaPlayer.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── LMMediaPlayer │ ├── Base.lproj │ │ └── Main.storyboard │ ├── IMG_2380.MOV │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── LMAppDelegate.h │ ├── LMAppDelegate.m │ ├── LMMediaPlayer-Info.plist │ ├── LMMediaPlayer-Prefix.pch │ ├── LMViewController.h │ ├── LMViewController.m │ ├── Launch Screen.storyboard │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── main.m │ └── sample.mp4 ├── Podfile ├── Podfile.lock └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── LMMediaPlayer.podspec ├── Pod ├── Assets │ ├── .gitkeep │ ├── LMMediaPlayerView.bundle │ │ ├── 6 │ │ │ ├── fullscreen.png │ │ │ ├── fullscreen@2x.png │ │ │ ├── pause.png │ │ │ ├── pause@2x.png │ │ │ ├── play.png │ │ │ ├── play@2x.png │ │ │ ├── repeat_all.png │ │ │ ├── repeat_all@2x.png │ │ │ ├── repeat_none.png │ │ │ ├── repeat_none@2x.png │ │ │ ├── repeat_one.png │ │ │ ├── repeat_one@2x.png │ │ │ ├── shuffle.png │ │ │ ├── shuffle@2x.png │ │ │ ├── unfullscreen.png │ │ │ ├── unfullscreen@2x.png │ │ │ ├── unshuffle.png │ │ │ └── unshuffle@2x.png │ │ └── 7 │ │ │ ├── fullscreen.png │ │ │ ├── fullscreen@2x.png │ │ │ ├── pause.png │ │ │ ├── pause@2x.png │ │ │ ├── play.png │ │ │ ├── play@2x.png │ │ │ ├── repeat_all.png │ │ │ ├── repeat_all@2x.png │ │ │ ├── repeat_none.png │ │ │ ├── repeat_none@2x.png │ │ │ ├── repeat_one.png │ │ │ ├── repeat_one@2x.png │ │ │ ├── shuffle.png │ │ │ ├── shuffle@2x.png │ │ │ ├── unfullscreen.png │ │ │ ├── unfullscreen@2x.png │ │ │ ├── unshuffle.png │ │ │ └── unshuffle@2x.png │ └── LMMediaPlayerView.xib └── Classes │ ├── .gitkeep │ ├── Category │ └── NSArray+LMMediaPlayerShuffle │ │ ├── NSArray+LMMediaPlayerShuffle.h │ │ └── NSArray+LMMediaPlayerShuffle.m │ ├── Hepler │ ├── LMMediaPlayerHelper.h │ └── LMMediaPlayerHelper.m │ ├── LMProgressBarView │ ├── LMProgressBarView.h │ └── LMProgressBarView.m │ ├── MediaPlayer │ ├── LMMediaItem │ │ ├── LMMediaItem.h │ │ └── LMMediaItem.m │ ├── LMMediaItemQueueManager │ │ ├── LMMediaItemQueueManager.h │ │ └── LMMediaItemQueueManager.m │ ├── LMMediaItemStreamingCache │ │ ├── LMMediaItemStreamingCache.h │ │ └── LMMediaItemStreamingCache.m │ └── LMMediaPlayer │ │ ├── LMMediaPlayer.h │ │ └── LMMediaPlayer.m │ └── MediaPlayerView │ ├── LMMediaPlayerView.h │ └── LMMediaPlayerView.m ├── README.md ├── images ├── 1.png ├── 2.png └── 3.png └── sample.mp4 /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: WebKit 2 | UseTab: Always 3 | IndentWidth: 8 4 | PointerBindsToType: false 5 | IndentCaseLabels: true 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/macos,swift,xcode,carthage,fastlane,cocoapods,objective-c,ruby 2 | 3 | ### Carthage ### 4 | # Carthage 5 | # 6 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 7 | # Carthage/Checkouts 8 | 9 | Carthage/Build 10 | 11 | ### CocoaPods ### 12 | ## CocoaPods GitIgnore Template 13 | 14 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing 15 | # - Also handy if you have a large number of dependant pods 16 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE 17 | Pods/ 18 | 19 | ### fastlane ### 20 | # fastlane - A streamlined workflow tool for Cocoa deployment 21 | # 22 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 23 | # screenshots whenever they are needed. 24 | # For more information about the recommended setup visit: 25 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 26 | 27 | # fastlane specific 28 | fastlane/report.xml 29 | 30 | # deliver temporary files 31 | fastlane/Preview.html 32 | 33 | # snapshot generated screenshots 34 | # fastlane/screenshots/**/*.png 35 | fastlane/screenshots/screenshots.html 36 | 37 | # scan temporary files 38 | fastlane/test_output 39 | ### macOS ### 40 | *.DS_Store 41 | .AppleDouble 42 | .LSOverride 43 | 44 | # Icon must end with two \r 45 | Icon 46 | 47 | # Thumbnails 48 | ._* 49 | 50 | # Files that might appear in the root of a volume 51 | .DocumentRevisions-V100 52 | .fseventsd 53 | .Spotlight-V100 54 | .TemporaryItems 55 | .Trashes 56 | .VolumeIcon.icns 57 | .com.apple.timemachine.donotpresent 58 | 59 | # Directories potentially created on remote AFP share 60 | .AppleDB 61 | .AppleDesktop 62 | Network Trash Folder 63 | Temporary Items 64 | .apdisk 65 | 66 | ### Objective-C ### 67 | # Xcode 68 | # 69 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 70 | 71 | ## Build generated 72 | build/ 73 | DerivedData/ 74 | 75 | ## Various settings 76 | *.pbxuser 77 | !default.pbxuser 78 | *.mode1v3 79 | !default.mode1v3 80 | *.mode2v3 81 | !default.mode2v3 82 | *.perspectivev3 83 | !default.perspectivev3 84 | xcuserdata/ 85 | 86 | ## Other 87 | *.moved-aside 88 | *.xccheckout 89 | *.xcscmblueprint 90 | 91 | ## Obj-C/Swift specific 92 | *.hmap 93 | *.ipa 94 | *.dSYM.zip 95 | *.dSYM 96 | 97 | # CocoaPods - Refactored to standalone file 98 | 99 | 100 | # Carthage - Refactored to standalone file 101 | 102 | # Code Injection 103 | # 104 | # After new code Injection tools there's a generated folder /iOSInjectionProject 105 | # https://github.com/johnno1962/injectionforxcode 106 | 107 | iOSInjectionProject/ 108 | 109 | ### Objective-C Patch ### 110 | 111 | ### Ruby ### 112 | *.gem 113 | *.rbc 114 | /.config 115 | /coverage/ 116 | /InstalledFiles 117 | /pkg/ 118 | /spec/reports/ 119 | /spec/examples.txt 120 | /test/tmp/ 121 | /test/version_tmp/ 122 | /tmp/ 123 | 124 | # Used by dotenv library to load environment variables. 125 | # .env 126 | 127 | ## Specific to RubyMotion: 128 | .dat* 129 | .repl_history 130 | *.bridgesupport 131 | build-iPhoneOS/ 132 | build-iPhoneSimulator/ 133 | 134 | ## Specific to RubyMotion (use of CocoaPods): 135 | # 136 | # We recommend against adding the Pods directory to your .gitignore. However 137 | # you should judge for yourself, the pros and cons are mentioned at: 138 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 139 | # 140 | # vendor/Pods/ 141 | 142 | ## Documentation cache and generated files: 143 | /.yardoc/ 144 | /_yardoc/ 145 | /doc/ 146 | /rdoc/ 147 | 148 | ## Environment normalization: 149 | /.bundle/ 150 | /vendor/bundle 151 | /lib/bundler/man/ 152 | 153 | # for a library or gem, you might want to ignore these files since the code is 154 | # intended to run in multiple environments; otherwise, check them in: 155 | # Gemfile.lock 156 | # .ruby-version 157 | # .ruby-gemset 158 | 159 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 160 | .rvmrc 161 | 162 | ### Swift ### 163 | # Xcode 164 | # 165 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 166 | 167 | ## Build generated 168 | 169 | ## Various settings 170 | 171 | ## Other 172 | 173 | ## Obj-C/Swift specific 174 | 175 | ## Playgrounds 176 | timeline.xctimeline 177 | playground.xcworkspace 178 | 179 | # Swift Package Manager 180 | # 181 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 182 | # Packages/ 183 | # Package.pins 184 | .build/ 185 | 186 | # CocoaPods - Refactored to standalone file 187 | 188 | # Carthage - Refactored to standalone file 189 | 190 | # fastlane 191 | # 192 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 193 | # screenshots whenever they are needed. 194 | # For more information about the recommended setup visit: 195 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 196 | 197 | 198 | ### Xcode ### 199 | # Xcode 200 | # 201 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 202 | 203 | ## Build generated 204 | 205 | ## Various settings 206 | 207 | ## Other 208 | 209 | ### Xcode Patch ### 210 | *.xcodeproj/* 211 | !*.xcodeproj/project.pbxproj 212 | !*.xcodeproj/xcshareddata/ 213 | !*.xcworkspace/contents.xcworkspacedata 214 | /*.gcno 215 | 216 | # End of https://www.gitignore.io/api/macos,swift,xcode,carthage,fastlane,cocoapods,objective-c,ruby -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # reference: http://www.objc.io/issue-6/travis-ci.html 2 | 3 | language: objective-c 4 | # before_install: cd Example && pod install && cd - 5 | script: 6 | - xctool test -workspace Example/LMMediaPlayer.xcworkspace -scheme LMMediaPlayer -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO 7 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3B2AD05B3C734255AACEE467 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C37A2301A7254035914241AB /* libPods-Tests.a */; }; 11 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 12 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 13 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 14 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 15 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 16 | 6003F5A1195388D20070C39A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6003F59F195388D20070C39A /* Main.storyboard */; }; 17 | 6003F5A7195388D20070C39A /* LMViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* LMViewController.m */; }; 18 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 19 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 20 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 21 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 22 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 23 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 24 | A73668C881864D758DB9CE8E /* libPods-LMMediaPlayer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E47F90859134B90894A0A06 /* libPods-LMMediaPlayer.a */; }; 25 | E94B353D19AF14B5001889E8 /* sample.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = E94B353C19AF14A6001889E8 /* sample.mp4 */; }; 26 | E95F59F91A86F2FF00F815F3 /* IMG_2380.MOV in Resources */ = {isa = PBXBuildFile; fileRef = E95F59F81A86F2FF00F815F3 /* IMG_2380.MOV */; }; 27 | E98CB3F019C865AD0072A6F7 /* LMAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* LMAppDelegate.m */; }; 28 | E9ED3A861E88098F004AC3DD /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E9ED3A851E88098F004AC3DD /* Launch Screen.storyboard */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 6003F582195388D10070C39A /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 6003F589195388D20070C39A; 37 | remoteInfo = LMMediaPlayer; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 072ADF0C5AC0155C9970318C /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = ""; }; 43 | 123AC9E9E549AB03F6BEF96E /* Pods-LMMediaPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMMediaPlayer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LMMediaPlayer/Pods-LMMediaPlayer.debug.xcconfig"; sourceTree = ""; }; 44 | 1E47F90859134B90894A0A06 /* libPods-LMMediaPlayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LMMediaPlayer.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 5B7B8EE23F00C051636CF067 /* Pods-LMMediaPlayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LMMediaPlayer.release.xcconfig"; path = "Pods/Target Support Files/Pods-LMMediaPlayer/Pods-LMMediaPlayer.release.xcconfig"; sourceTree = ""; }; 46 | 6003F58A195388D20070C39A /* LMMediaPlayer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LMMediaPlayer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 48 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 49 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 50 | 6003F595195388D20070C39A /* LMMediaPlayer-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LMMediaPlayer-Info.plist"; sourceTree = ""; }; 51 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 52 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 53 | 6003F59B195388D20070C39A /* LMMediaPlayer-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LMMediaPlayer-Prefix.pch"; sourceTree = ""; }; 54 | 6003F59C195388D20070C39A /* LMAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMAppDelegate.h; sourceTree = ""; }; 55 | 6003F59D195388D20070C39A /* LMAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMAppDelegate.m; sourceTree = ""; }; 56 | 6003F5A0195388D20070C39A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 57 | 6003F5A5195388D20070C39A /* LMViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMViewController.h; sourceTree = ""; }; 58 | 6003F5A6195388D20070C39A /* LMViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMViewController.m; sourceTree = ""; }; 59 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 60 | 6003F5AE195388D20070C39A /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 62 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 63 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 64 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 65 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 66 | 9EBB4FFB1B8DA3CE5F26C6FA /* LMMediaPlayer.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LMMediaPlayer.podspec; path = ../LMMediaPlayer.podspec; sourceTree = ""; }; 67 | B3B69134C05EDDD1F703D275 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 68 | BB0BEC98945053160D841919 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = ""; }; 69 | C37A2301A7254035914241AB /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | D18EC80BE5E8BFB35C5823D4 /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 71 | E94B352219AF1267001889E8 /* NSArray+LMMediaPlayerShuffle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+LMMediaPlayerShuffle.h"; sourceTree = ""; }; 72 | E94B352319AF1267001889E8 /* NSArray+LMMediaPlayerShuffle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+LMMediaPlayerShuffle.m"; sourceTree = ""; }; 73 | E94B352619AF1267001889E8 /* LMMediaItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaItem.h; sourceTree = ""; }; 74 | E94B352719AF1267001889E8 /* LMMediaItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaItem.m; sourceTree = ""; }; 75 | E94B352919AF1267001889E8 /* LMMediaItemQueueManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaItemQueueManager.h; sourceTree = ""; }; 76 | E94B352A19AF1267001889E8 /* LMMediaItemQueueManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaItemQueueManager.m; sourceTree = ""; }; 77 | E94B352C19AF1267001889E8 /* LMMediaPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaPlayer.h; sourceTree = ""; }; 78 | E94B352D19AF1267001889E8 /* LMMediaPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaPlayer.m; sourceTree = ""; }; 79 | E94B352F19AF1267001889E8 /* LMMediaPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaPlayerView.h; sourceTree = ""; }; 80 | E94B353019AF1267001889E8 /* LMMediaPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaPlayerView.m; sourceTree = ""; }; 81 | E94B353C19AF14A6001889E8 /* sample.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = sample.mp4; sourceTree = ""; }; 82 | E95F59F81A86F2FF00F815F3 /* IMG_2380.MOV */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; path = IMG_2380.MOV; sourceTree = ""; }; 83 | E980AF2119C85CEA003FF2AF /* LMMediaPlayerView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = LMMediaPlayerView.bundle; sourceTree = ""; }; 84 | E996AAB41A74425A00C2F7BE /* LMMediaItemStreamingCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaItemStreamingCache.h; sourceTree = ""; }; 85 | E996AAB51A74425A00C2F7BE /* LMMediaItemStreamingCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaItemStreamingCache.m; sourceTree = ""; }; 86 | E9B7202C19B2C6F700F7E4D1 /* LMMediaPlayerHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMMediaPlayerHelper.h; sourceTree = ""; }; 87 | E9B7202D19B2C6F700F7E4D1 /* LMMediaPlayerHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMMediaPlayerHelper.m; sourceTree = ""; }; 88 | E9B862441C1974AB00A49EF9 /* LMProgressBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LMProgressBarView.h; sourceTree = ""; }; 89 | E9B862451C1974AB00A49EF9 /* LMProgressBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LMProgressBarView.m; sourceTree = ""; }; 90 | E9B890A719C8B1DF00F22638 /* LMMediaPlayerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LMMediaPlayerView.xib; sourceTree = ""; }; 91 | E9ED3A851E88098F004AC3DD /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | 6003F587195388D20070C39A /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 100 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 101 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 102 | A73668C881864D758DB9CE8E /* libPods-LMMediaPlayer.a in Frameworks */, 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 6003F5AB195388D20070C39A /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 111 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 112 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 113 | 3B2AD05B3C734255AACEE467 /* libPods-Tests.a in Frameworks */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | /* End PBXFrameworksBuildPhase section */ 118 | 119 | /* Begin PBXGroup section */ 120 | 54A8416F262C076E48294FE0 /* Pods */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 123AC9E9E549AB03F6BEF96E /* Pods-LMMediaPlayer.debug.xcconfig */, 124 | 5B7B8EE23F00C051636CF067 /* Pods-LMMediaPlayer.release.xcconfig */, 125 | D18EC80BE5E8BFB35C5823D4 /* Pods-Tests.debug.xcconfig */, 126 | 072ADF0C5AC0155C9970318C /* Pods-Tests.release.xcconfig */, 127 | ); 128 | name = Pods; 129 | sourceTree = ""; 130 | }; 131 | 6003F581195388D10070C39A = { 132 | isa = PBXGroup; 133 | children = ( 134 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 135 | 6003F593195388D20070C39A /* LMMediaPlayer */, 136 | 6003F5B5195388D20070C39A /* Tests */, 137 | 6003F58C195388D20070C39A /* Frameworks */, 138 | 6003F58B195388D20070C39A /* Products */, 139 | 54A8416F262C076E48294FE0 /* Pods */, 140 | ); 141 | sourceTree = ""; 142 | }; 143 | 6003F58B195388D20070C39A /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 6003F58A195388D20070C39A /* LMMediaPlayer.app */, 147 | 6003F5AE195388D20070C39A /* Tests.xctest */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | 6003F58C195388D20070C39A /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 6003F58D195388D20070C39A /* Foundation.framework */, 156 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 157 | 6003F591195388D20070C39A /* UIKit.framework */, 158 | 6003F5AF195388D20070C39A /* XCTest.framework */, 159 | 1E47F90859134B90894A0A06 /* libPods-LMMediaPlayer.a */, 160 | C37A2301A7254035914241AB /* libPods-Tests.a */, 161 | ); 162 | name = Frameworks; 163 | sourceTree = ""; 164 | }; 165 | 6003F593195388D20070C39A /* LMMediaPlayer */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | E94B351919AF1267001889E8 /* Pod */, 169 | 6003F59C195388D20070C39A /* LMAppDelegate.h */, 170 | 6003F59D195388D20070C39A /* LMAppDelegate.m */, 171 | E9ED3A851E88098F004AC3DD /* Launch Screen.storyboard */, 172 | 6003F59F195388D20070C39A /* Main.storyboard */, 173 | 6003F5A5195388D20070C39A /* LMViewController.h */, 174 | 6003F5A6195388D20070C39A /* LMViewController.m */, 175 | E95F59F81A86F2FF00F815F3 /* IMG_2380.MOV */, 176 | E94B353C19AF14A6001889E8 /* sample.mp4 */, 177 | 6003F5A8195388D20070C39A /* Images.xcassets */, 178 | 6003F594195388D20070C39A /* Supporting Files */, 179 | ); 180 | path = LMMediaPlayer; 181 | sourceTree = ""; 182 | }; 183 | 6003F594195388D20070C39A /* Supporting Files */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 6003F595195388D20070C39A /* LMMediaPlayer-Info.plist */, 187 | 6003F596195388D20070C39A /* InfoPlist.strings */, 188 | 6003F599195388D20070C39A /* main.m */, 189 | 6003F59B195388D20070C39A /* LMMediaPlayer-Prefix.pch */, 190 | ); 191 | name = "Supporting Files"; 192 | sourceTree = ""; 193 | }; 194 | 6003F5B5195388D20070C39A /* Tests */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 6003F5BB195388D20070C39A /* Tests.m */, 198 | 6003F5B6195388D20070C39A /* Supporting Files */, 199 | ); 200 | path = Tests; 201 | sourceTree = ""; 202 | }; 203 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 207 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 208 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 209 | ); 210 | name = "Supporting Files"; 211 | sourceTree = ""; 212 | }; 213 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | 9EBB4FFB1B8DA3CE5F26C6FA /* LMMediaPlayer.podspec */, 217 | BB0BEC98945053160D841919 /* README.md */, 218 | B3B69134C05EDDD1F703D275 /* LICENSE */, 219 | ); 220 | name = "Podspec Metadata"; 221 | sourceTree = ""; 222 | }; 223 | E94B351919AF1267001889E8 /* Pod */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | E94B351A19AF1267001889E8 /* Assets */, 227 | E94B351E19AF1267001889E8 /* Classes */, 228 | ); 229 | name = Pod; 230 | path = ../../Pod; 231 | sourceTree = ""; 232 | }; 233 | E94B351A19AF1267001889E8 /* Assets */ = { 234 | isa = PBXGroup; 235 | children = ( 236 | E9B890A719C8B1DF00F22638 /* LMMediaPlayerView.xib */, 237 | E980AF2119C85CEA003FF2AF /* LMMediaPlayerView.bundle */, 238 | ); 239 | path = Assets; 240 | sourceTree = ""; 241 | }; 242 | E94B351E19AF1267001889E8 /* Classes */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | E9B862431C1974AB00A49EF9 /* LMProgressBarView */, 246 | E94B352019AF1267001889E8 /* Category */, 247 | E9B7202B19B2C6CF00F7E4D1 /* Hepler */, 248 | E94B352419AF1267001889E8 /* MediaPlayer */, 249 | E94B352E19AF1267001889E8 /* MediaPlayerView */, 250 | ); 251 | path = Classes; 252 | sourceTree = ""; 253 | }; 254 | E94B352019AF1267001889E8 /* Category */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | E94B352119AF1267001889E8 /* NSArray+LMMediaPlayerShuffle */, 258 | ); 259 | path = Category; 260 | sourceTree = ""; 261 | }; 262 | E94B352119AF1267001889E8 /* NSArray+LMMediaPlayerShuffle */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | E94B352219AF1267001889E8 /* NSArray+LMMediaPlayerShuffle.h */, 266 | E94B352319AF1267001889E8 /* NSArray+LMMediaPlayerShuffle.m */, 267 | ); 268 | path = "NSArray+LMMediaPlayerShuffle"; 269 | sourceTree = ""; 270 | }; 271 | E94B352419AF1267001889E8 /* MediaPlayer */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | E996AAB31A74425A00C2F7BE /* LMMediaItemStreamingCache */, 275 | E94B352519AF1267001889E8 /* LMMediaItem */, 276 | E94B352819AF1267001889E8 /* LMMediaItemQueueManager */, 277 | E94B352B19AF1267001889E8 /* LMMediaPlayer */, 278 | ); 279 | path = MediaPlayer; 280 | sourceTree = ""; 281 | }; 282 | E94B352519AF1267001889E8 /* LMMediaItem */ = { 283 | isa = PBXGroup; 284 | children = ( 285 | E94B352619AF1267001889E8 /* LMMediaItem.h */, 286 | E94B352719AF1267001889E8 /* LMMediaItem.m */, 287 | ); 288 | path = LMMediaItem; 289 | sourceTree = ""; 290 | }; 291 | E94B352819AF1267001889E8 /* LMMediaItemQueueManager */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | E94B352919AF1267001889E8 /* LMMediaItemQueueManager.h */, 295 | E94B352A19AF1267001889E8 /* LMMediaItemQueueManager.m */, 296 | ); 297 | path = LMMediaItemQueueManager; 298 | sourceTree = ""; 299 | }; 300 | E94B352B19AF1267001889E8 /* LMMediaPlayer */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | E94B352C19AF1267001889E8 /* LMMediaPlayer.h */, 304 | E94B352D19AF1267001889E8 /* LMMediaPlayer.m */, 305 | ); 306 | path = LMMediaPlayer; 307 | sourceTree = ""; 308 | }; 309 | E94B352E19AF1267001889E8 /* MediaPlayerView */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | E94B352F19AF1267001889E8 /* LMMediaPlayerView.h */, 313 | E94B353019AF1267001889E8 /* LMMediaPlayerView.m */, 314 | ); 315 | path = MediaPlayerView; 316 | sourceTree = ""; 317 | }; 318 | E996AAB31A74425A00C2F7BE /* LMMediaItemStreamingCache */ = { 319 | isa = PBXGroup; 320 | children = ( 321 | E996AAB41A74425A00C2F7BE /* LMMediaItemStreamingCache.h */, 322 | E996AAB51A74425A00C2F7BE /* LMMediaItemStreamingCache.m */, 323 | ); 324 | path = LMMediaItemStreamingCache; 325 | sourceTree = ""; 326 | }; 327 | E9B7202B19B2C6CF00F7E4D1 /* Hepler */ = { 328 | isa = PBXGroup; 329 | children = ( 330 | E9B7202C19B2C6F700F7E4D1 /* LMMediaPlayerHelper.h */, 331 | E9B7202D19B2C6F700F7E4D1 /* LMMediaPlayerHelper.m */, 332 | ); 333 | path = Hepler; 334 | sourceTree = ""; 335 | }; 336 | E9B862431C1974AB00A49EF9 /* LMProgressBarView */ = { 337 | isa = PBXGroup; 338 | children = ( 339 | E9B862441C1974AB00A49EF9 /* LMProgressBarView.h */, 340 | E9B862451C1974AB00A49EF9 /* LMProgressBarView.m */, 341 | ); 342 | path = LMProgressBarView; 343 | sourceTree = ""; 344 | }; 345 | /* End PBXGroup section */ 346 | 347 | /* Begin PBXNativeTarget section */ 348 | 6003F589195388D20070C39A /* LMMediaPlayer */ = { 349 | isa = PBXNativeTarget; 350 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "LMMediaPlayer" */; 351 | buildPhases = ( 352 | 7071D1C914EB72FC11B8DDBA /* [CP] Check Pods Manifest.lock */, 353 | D9247A78CBBC45EC96B032A6 /* Check Pods Manifest.lock */, 354 | 6003F586195388D20070C39A /* Sources */, 355 | 6003F587195388D20070C39A /* Frameworks */, 356 | 6003F588195388D20070C39A /* Resources */, 357 | 52D0F047853E4FE6A1A9343E /* Copy Pods Resources */, 358 | 9D19B80D09E823EC3D649358 /* [CP] Copy Pods Resources */, 359 | ); 360 | buildRules = ( 361 | ); 362 | dependencies = ( 363 | ); 364 | name = LMMediaPlayer; 365 | productName = LMMediaPlayer; 366 | productReference = 6003F58A195388D20070C39A /* LMMediaPlayer.app */; 367 | productType = "com.apple.product-type.application"; 368 | }; 369 | 6003F5AD195388D20070C39A /* Tests */ = { 370 | isa = PBXNativeTarget; 371 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */; 372 | buildPhases = ( 373 | F378291971E2C8D43692A91D /* [CP] Check Pods Manifest.lock */, 374 | 6C8FFBF12EF94CA2AD7E703C /* Check Pods Manifest.lock */, 375 | 6003F5AA195388D20070C39A /* Sources */, 376 | 6003F5AB195388D20070C39A /* Frameworks */, 377 | 6003F5AC195388D20070C39A /* Resources */, 378 | ACA9EF9967054833AB55A77D /* Copy Pods Resources */, 379 | 34FE677B256A899FEA0D49E9 /* [CP] Copy Pods Resources */, 380 | ); 381 | buildRules = ( 382 | ); 383 | dependencies = ( 384 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 385 | ); 386 | name = Tests; 387 | productName = LMMediaPlayerTests; 388 | productReference = 6003F5AE195388D20070C39A /* Tests.xctest */; 389 | productType = "com.apple.product-type.bundle.unit-test"; 390 | }; 391 | /* End PBXNativeTarget section */ 392 | 393 | /* Begin PBXProject section */ 394 | 6003F582195388D10070C39A /* Project object */ = { 395 | isa = PBXProject; 396 | attributes = { 397 | CLASSPREFIX = LM; 398 | LastUpgradeCheck = 1020; 399 | ORGANIZATIONNAME = "Akira Matsuda"; 400 | TargetAttributes = { 401 | 6003F589195388D20070C39A = { 402 | SystemCapabilities = { 403 | com.apple.BackgroundModes = { 404 | enabled = 1; 405 | }; 406 | }; 407 | }; 408 | 6003F5AD195388D20070C39A = { 409 | TestTargetID = 6003F589195388D20070C39A; 410 | }; 411 | }; 412 | }; 413 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "LMMediaPlayer" */; 414 | compatibilityVersion = "Xcode 3.2"; 415 | developmentRegion = en; 416 | hasScannedForEncodings = 0; 417 | knownRegions = ( 418 | en, 419 | Base, 420 | ); 421 | mainGroup = 6003F581195388D10070C39A; 422 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 423 | projectDirPath = ""; 424 | projectRoot = ""; 425 | targets = ( 426 | 6003F589195388D20070C39A /* LMMediaPlayer */, 427 | 6003F5AD195388D20070C39A /* Tests */, 428 | ); 429 | }; 430 | /* End PBXProject section */ 431 | 432 | /* Begin PBXResourcesBuildPhase section */ 433 | 6003F588195388D20070C39A /* Resources */ = { 434 | isa = PBXResourcesBuildPhase; 435 | buildActionMask = 2147483647; 436 | files = ( 437 | E9ED3A861E88098F004AC3DD /* Launch Screen.storyboard in Resources */, 438 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 439 | 6003F5A1195388D20070C39A /* Main.storyboard in Resources */, 440 | E95F59F91A86F2FF00F815F3 /* IMG_2380.MOV in Resources */, 441 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 442 | E94B353D19AF14B5001889E8 /* sample.mp4 in Resources */, 443 | ); 444 | runOnlyForDeploymentPostprocessing = 0; 445 | }; 446 | 6003F5AC195388D20070C39A /* Resources */ = { 447 | isa = PBXResourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 451 | ); 452 | runOnlyForDeploymentPostprocessing = 0; 453 | }; 454 | /* End PBXResourcesBuildPhase section */ 455 | 456 | /* Begin PBXShellScriptBuildPhase section */ 457 | 34FE677B256A899FEA0D49E9 /* [CP] Copy Pods Resources */ = { 458 | isa = PBXShellScriptBuildPhase; 459 | buildActionMask = 2147483647; 460 | files = ( 461 | ); 462 | inputFileListPaths = ( 463 | ); 464 | inputPaths = ( 465 | "${PODS_ROOT}/Target Support Files/Pods-Tests/Pods-Tests-resources.sh", 466 | "${PODS_ROOT}/../../Pod/Assets/LMMediaPlayerView.xib", 467 | "${PODS_ROOT}/../../Pod/Assets/LMMediaPlayerView.bundle", 468 | ); 469 | name = "[CP] Copy Pods Resources"; 470 | outputFileListPaths = ( 471 | ); 472 | outputPaths = ( 473 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LMMediaPlayerView.nib", 474 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LMMediaPlayerView.bundle", 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | shellPath = /bin/sh; 478 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tests/Pods-Tests-resources.sh\"\n"; 479 | showEnvVarsInLog = 0; 480 | }; 481 | 52D0F047853E4FE6A1A9343E /* Copy Pods Resources */ = { 482 | isa = PBXShellScriptBuildPhase; 483 | buildActionMask = 2147483647; 484 | files = ( 485 | ); 486 | inputPaths = ( 487 | ); 488 | name = "Copy Pods Resources"; 489 | outputPaths = ( 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | shellPath = /bin/sh; 493 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LMMediaPlayer/Pods-LMMediaPlayer-resources.sh\"\n"; 494 | showEnvVarsInLog = 0; 495 | }; 496 | 6C8FFBF12EF94CA2AD7E703C /* Check Pods Manifest.lock */ = { 497 | isa = PBXShellScriptBuildPhase; 498 | buildActionMask = 2147483647; 499 | files = ( 500 | ); 501 | inputPaths = ( 502 | ); 503 | name = "Check Pods Manifest.lock"; 504 | outputPaths = ( 505 | ); 506 | runOnlyForDeploymentPostprocessing = 0; 507 | shellPath = /bin/sh; 508 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 509 | showEnvVarsInLog = 0; 510 | }; 511 | 7071D1C914EB72FC11B8DDBA /* [CP] Check Pods Manifest.lock */ = { 512 | isa = PBXShellScriptBuildPhase; 513 | buildActionMask = 2147483647; 514 | files = ( 515 | ); 516 | inputFileListPaths = ( 517 | ); 518 | inputPaths = ( 519 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 520 | "${PODS_ROOT}/Manifest.lock", 521 | ); 522 | name = "[CP] Check Pods Manifest.lock"; 523 | outputFileListPaths = ( 524 | ); 525 | outputPaths = ( 526 | "$(DERIVED_FILE_DIR)/Pods-LMMediaPlayer-checkManifestLockResult.txt", 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | shellPath = /bin/sh; 530 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 531 | showEnvVarsInLog = 0; 532 | }; 533 | 9D19B80D09E823EC3D649358 /* [CP] Copy Pods Resources */ = { 534 | isa = PBXShellScriptBuildPhase; 535 | buildActionMask = 2147483647; 536 | files = ( 537 | ); 538 | inputFileListPaths = ( 539 | ); 540 | inputPaths = ( 541 | "${PODS_ROOT}/Target Support Files/Pods-LMMediaPlayer/Pods-LMMediaPlayer-resources.sh", 542 | "${PODS_ROOT}/../../Pod/Assets/LMMediaPlayerView.xib", 543 | "${PODS_ROOT}/../../Pod/Assets/LMMediaPlayerView.bundle", 544 | ); 545 | name = "[CP] Copy Pods Resources"; 546 | outputFileListPaths = ( 547 | ); 548 | outputPaths = ( 549 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LMMediaPlayerView.nib", 550 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LMMediaPlayerView.bundle", 551 | ); 552 | runOnlyForDeploymentPostprocessing = 0; 553 | shellPath = /bin/sh; 554 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LMMediaPlayer/Pods-LMMediaPlayer-resources.sh\"\n"; 555 | showEnvVarsInLog = 0; 556 | }; 557 | ACA9EF9967054833AB55A77D /* Copy Pods Resources */ = { 558 | isa = PBXShellScriptBuildPhase; 559 | buildActionMask = 2147483647; 560 | files = ( 561 | ); 562 | inputPaths = ( 563 | ); 564 | name = "Copy Pods Resources"; 565 | outputPaths = ( 566 | ); 567 | runOnlyForDeploymentPostprocessing = 0; 568 | shellPath = /bin/sh; 569 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests/Pods-Tests-resources.sh\"\n"; 570 | showEnvVarsInLog = 0; 571 | }; 572 | D9247A78CBBC45EC96B032A6 /* Check Pods Manifest.lock */ = { 573 | isa = PBXShellScriptBuildPhase; 574 | buildActionMask = 2147483647; 575 | files = ( 576 | ); 577 | inputPaths = ( 578 | ); 579 | name = "Check Pods Manifest.lock"; 580 | outputPaths = ( 581 | ); 582 | runOnlyForDeploymentPostprocessing = 0; 583 | shellPath = /bin/sh; 584 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 585 | showEnvVarsInLog = 0; 586 | }; 587 | F378291971E2C8D43692A91D /* [CP] Check Pods Manifest.lock */ = { 588 | isa = PBXShellScriptBuildPhase; 589 | buildActionMask = 2147483647; 590 | files = ( 591 | ); 592 | inputFileListPaths = ( 593 | ); 594 | inputPaths = ( 595 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 596 | "${PODS_ROOT}/Manifest.lock", 597 | ); 598 | name = "[CP] Check Pods Manifest.lock"; 599 | outputFileListPaths = ( 600 | ); 601 | outputPaths = ( 602 | "$(DERIVED_FILE_DIR)/Pods-Tests-checkManifestLockResult.txt", 603 | ); 604 | runOnlyForDeploymentPostprocessing = 0; 605 | shellPath = /bin/sh; 606 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 607 | showEnvVarsInLog = 0; 608 | }; 609 | /* End PBXShellScriptBuildPhase section */ 610 | 611 | /* Begin PBXSourcesBuildPhase section */ 612 | 6003F586195388D20070C39A /* Sources */ = { 613 | isa = PBXSourcesBuildPhase; 614 | buildActionMask = 2147483647; 615 | files = ( 616 | 6003F5A7195388D20070C39A /* LMViewController.m in Sources */, 617 | 6003F59A195388D20070C39A /* main.m in Sources */, 618 | E98CB3F019C865AD0072A6F7 /* LMAppDelegate.m in Sources */, 619 | ); 620 | runOnlyForDeploymentPostprocessing = 0; 621 | }; 622 | 6003F5AA195388D20070C39A /* Sources */ = { 623 | isa = PBXSourcesBuildPhase; 624 | buildActionMask = 2147483647; 625 | files = ( 626 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 627 | ); 628 | runOnlyForDeploymentPostprocessing = 0; 629 | }; 630 | /* End PBXSourcesBuildPhase section */ 631 | 632 | /* Begin PBXTargetDependency section */ 633 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 634 | isa = PBXTargetDependency; 635 | target = 6003F589195388D20070C39A /* LMMediaPlayer */; 636 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 637 | }; 638 | /* End PBXTargetDependency section */ 639 | 640 | /* Begin PBXVariantGroup section */ 641 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 642 | isa = PBXVariantGroup; 643 | children = ( 644 | 6003F597195388D20070C39A /* en */, 645 | ); 646 | name = InfoPlist.strings; 647 | sourceTree = ""; 648 | }; 649 | 6003F59F195388D20070C39A /* Main.storyboard */ = { 650 | isa = PBXVariantGroup; 651 | children = ( 652 | 6003F5A0195388D20070C39A /* Base */, 653 | ); 654 | name = Main.storyboard; 655 | sourceTree = ""; 656 | }; 657 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 658 | isa = PBXVariantGroup; 659 | children = ( 660 | 6003F5B9195388D20070C39A /* en */, 661 | ); 662 | name = InfoPlist.strings; 663 | sourceTree = ""; 664 | }; 665 | /* End PBXVariantGroup section */ 666 | 667 | /* Begin XCBuildConfiguration section */ 668 | 6003F5BD195388D20070C39A /* Debug */ = { 669 | isa = XCBuildConfiguration; 670 | buildSettings = { 671 | ALWAYS_SEARCH_USER_PATHS = NO; 672 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 673 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 674 | CLANG_CXX_LIBRARY = "libc++"; 675 | CLANG_ENABLE_MODULES = YES; 676 | CLANG_ENABLE_OBJC_ARC = YES; 677 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 678 | CLANG_WARN_BOOL_CONVERSION = YES; 679 | CLANG_WARN_COMMA = YES; 680 | CLANG_WARN_CONSTANT_CONVERSION = YES; 681 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 682 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 683 | CLANG_WARN_EMPTY_BODY = YES; 684 | CLANG_WARN_ENUM_CONVERSION = YES; 685 | CLANG_WARN_INFINITE_RECURSION = YES; 686 | CLANG_WARN_INT_CONVERSION = YES; 687 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 688 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 689 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 690 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 691 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 692 | CLANG_WARN_STRICT_PROTOTYPES = YES; 693 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 694 | CLANG_WARN_UNREACHABLE_CODE = YES; 695 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 696 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 697 | COPY_PHASE_STRIP = NO; 698 | ENABLE_STRICT_OBJC_MSGSEND = YES; 699 | ENABLE_TESTABILITY = YES; 700 | GCC_C_LANGUAGE_STANDARD = gnu99; 701 | GCC_DYNAMIC_NO_PIC = NO; 702 | GCC_NO_COMMON_BLOCKS = YES; 703 | GCC_OPTIMIZATION_LEVEL = 0; 704 | GCC_PREPROCESSOR_DEFINITIONS = ( 705 | "DEBUG=1", 706 | "$(inherited)", 707 | ); 708 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 709 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 710 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 711 | GCC_WARN_UNDECLARED_SELECTOR = YES; 712 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 713 | GCC_WARN_UNUSED_FUNCTION = YES; 714 | GCC_WARN_UNUSED_VARIABLE = YES; 715 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 716 | ONLY_ACTIVE_ARCH = YES; 717 | SDKROOT = iphoneos; 718 | TARGETED_DEVICE_FAMILY = "1,2"; 719 | }; 720 | name = Debug; 721 | }; 722 | 6003F5BE195388D20070C39A /* Release */ = { 723 | isa = XCBuildConfiguration; 724 | buildSettings = { 725 | ALWAYS_SEARCH_USER_PATHS = NO; 726 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 727 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 728 | CLANG_CXX_LIBRARY = "libc++"; 729 | CLANG_ENABLE_MODULES = YES; 730 | CLANG_ENABLE_OBJC_ARC = YES; 731 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 732 | CLANG_WARN_BOOL_CONVERSION = YES; 733 | CLANG_WARN_COMMA = YES; 734 | CLANG_WARN_CONSTANT_CONVERSION = YES; 735 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 736 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 737 | CLANG_WARN_EMPTY_BODY = YES; 738 | CLANG_WARN_ENUM_CONVERSION = YES; 739 | CLANG_WARN_INFINITE_RECURSION = YES; 740 | CLANG_WARN_INT_CONVERSION = YES; 741 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 742 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 743 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 744 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 745 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 746 | CLANG_WARN_STRICT_PROTOTYPES = YES; 747 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 748 | CLANG_WARN_UNREACHABLE_CODE = YES; 749 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 750 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 751 | COPY_PHASE_STRIP = YES; 752 | ENABLE_NS_ASSERTIONS = NO; 753 | ENABLE_STRICT_OBJC_MSGSEND = YES; 754 | GCC_C_LANGUAGE_STANDARD = gnu99; 755 | GCC_NO_COMMON_BLOCKS = YES; 756 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 757 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 758 | GCC_WARN_UNDECLARED_SELECTOR = YES; 759 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 760 | GCC_WARN_UNUSED_FUNCTION = YES; 761 | GCC_WARN_UNUSED_VARIABLE = YES; 762 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 763 | SDKROOT = iphoneos; 764 | TARGETED_DEVICE_FAMILY = "1,2"; 765 | VALIDATE_PRODUCT = YES; 766 | }; 767 | name = Release; 768 | }; 769 | 6003F5C0195388D20070C39A /* Debug */ = { 770 | isa = XCBuildConfiguration; 771 | baseConfigurationReference = 123AC9E9E549AB03F6BEF96E /* Pods-LMMediaPlayer.debug.xcconfig */; 772 | buildSettings = { 773 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 774 | CLANG_ENABLE_OBJC_ARC = NO; 775 | CLANG_ENABLE_OBJC_WEAK = YES; 776 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 777 | GCC_PREFIX_HEADER = "LMMediaPlayer/LMMediaPlayer-Prefix.pch"; 778 | INFOPLIST_FILE = "LMMediaPlayer/LMMediaPlayer-Info.plist"; 779 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 780 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 781 | PRODUCT_NAME = "$(TARGET_NAME)"; 782 | TARGETED_DEVICE_FAMILY = 1; 783 | WRAPPER_EXTENSION = app; 784 | }; 785 | name = Debug; 786 | }; 787 | 6003F5C1195388D20070C39A /* Release */ = { 788 | isa = XCBuildConfiguration; 789 | baseConfigurationReference = 5B7B8EE23F00C051636CF067 /* Pods-LMMediaPlayer.release.xcconfig */; 790 | buildSettings = { 791 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 792 | CLANG_ENABLE_OBJC_ARC = NO; 793 | CLANG_ENABLE_OBJC_WEAK = YES; 794 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 795 | GCC_PREFIX_HEADER = "LMMediaPlayer/LMMediaPlayer-Prefix.pch"; 796 | INFOPLIST_FILE = "LMMediaPlayer/LMMediaPlayer-Info.plist"; 797 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 798 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 799 | PRODUCT_NAME = "$(TARGET_NAME)"; 800 | TARGETED_DEVICE_FAMILY = 1; 801 | WRAPPER_EXTENSION = app; 802 | }; 803 | name = Release; 804 | }; 805 | 6003F5C3195388D20070C39A /* Debug */ = { 806 | isa = XCBuildConfiguration; 807 | baseConfigurationReference = D18EC80BE5E8BFB35C5823D4 /* Pods-Tests.debug.xcconfig */; 808 | buildSettings = { 809 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/LMMediaPlayer.app/LMMediaPlayer"; 810 | FRAMEWORK_SEARCH_PATHS = ( 811 | "$(SDKROOT)/Developer/Library/Frameworks", 812 | "$(inherited)", 813 | "$(DEVELOPER_FRAMEWORKS_DIR)", 814 | ); 815 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 816 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 817 | GCC_PREPROCESSOR_DEFINITIONS = ( 818 | "DEBUG=1", 819 | "$(inherited)", 820 | ); 821 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 822 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 823 | PRODUCT_NAME = "$(TARGET_NAME)"; 824 | TEST_HOST = "$(BUNDLE_LOADER)"; 825 | WRAPPER_EXTENSION = xctest; 826 | }; 827 | name = Debug; 828 | }; 829 | 6003F5C4195388D20070C39A /* Release */ = { 830 | isa = XCBuildConfiguration; 831 | baseConfigurationReference = 072ADF0C5AC0155C9970318C /* Pods-Tests.release.xcconfig */; 832 | buildSettings = { 833 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/LMMediaPlayer.app/LMMediaPlayer"; 834 | FRAMEWORK_SEARCH_PATHS = ( 835 | "$(SDKROOT)/Developer/Library/Frameworks", 836 | "$(inherited)", 837 | "$(DEVELOPER_FRAMEWORKS_DIR)", 838 | ); 839 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 840 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 841 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 842 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 843 | PRODUCT_NAME = "$(TARGET_NAME)"; 844 | TEST_HOST = "$(BUNDLE_LOADER)"; 845 | WRAPPER_EXTENSION = xctest; 846 | }; 847 | name = Release; 848 | }; 849 | /* End XCBuildConfiguration section */ 850 | 851 | /* Begin XCConfigurationList section */ 852 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "LMMediaPlayer" */ = { 853 | isa = XCConfigurationList; 854 | buildConfigurations = ( 855 | 6003F5BD195388D20070C39A /* Debug */, 856 | 6003F5BE195388D20070C39A /* Release */, 857 | ); 858 | defaultConfigurationIsVisible = 0; 859 | defaultConfigurationName = Release; 860 | }; 861 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "LMMediaPlayer" */ = { 862 | isa = XCConfigurationList; 863 | buildConfigurations = ( 864 | 6003F5C0195388D20070C39A /* Debug */, 865 | 6003F5C1195388D20070C39A /* Release */, 866 | ); 867 | defaultConfigurationIsVisible = 0; 868 | defaultConfigurationName = Release; 869 | }; 870 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */ = { 871 | isa = XCConfigurationList; 872 | buildConfigurations = ( 873 | 6003F5C3195388D20070C39A /* Debug */, 874 | 6003F5C4195388D20070C39A /* Release */, 875 | ); 876 | defaultConfigurationIsVisible = 0; 877 | defaultConfigurationName = Release; 878 | }; 879 | /* End XCConfigurationList section */ 880 | }; 881 | rootObject = 6003F582195388D10070C39A /* Project object */; 882 | } 883 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcodeproj/xcshareddata/xcschemes/LMMediaPlayer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/IMG_2380.MOV: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Example/LMMediaPlayer/IMG_2380.MOV -------------------------------------------------------------------------------- /Example/LMMediaPlayer/Images.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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Example/LMMediaPlayer/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "ipad", 45 | "extent" : "full-screen", 46 | "minimum-system-version" : "7.0", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "orientation" : "landscape", 51 | "idiom" : "ipad", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "1x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "extent" : "full-screen", 60 | "minimum-system-version" : "7.0", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "orientation" : "landscape", 65 | "idiom" : "ipad", 66 | "extent" : "full-screen", 67 | "minimum-system-version" : "7.0", 68 | "scale" : "2x" 69 | } 70 | ], 71 | "info" : { 72 | "version" : 1, 73 | "author" : "xcode" 74 | } 75 | } -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMAppDelegate.h 3 | // LMMediaPlayer 4 | // 5 | // Created by CocoaPods on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMAppDelegate.m 3 | // LMMediaPlayer 4 | // 5 | // Created by CocoaPods on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMAppDelegate.h" 10 | 11 | @implementation LMAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMMediaPlayer-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | NSAppleMusicUsageDescription 33 | Test 34 | UIBackgroundModes 35 | 36 | audio 37 | 38 | UILaunchStoryboardName 39 | Launch Screen 40 | UIMainStoryboardFile 41 | Main 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMMediaPlayer-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMViewController.h 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LMMediaPlayerView.h" 11 | 12 | @interface LMViewController : UITableViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/LMViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMViewController.m 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMViewController.h" 10 | #import 11 | #import "LMMediaItem.h" 12 | 13 | @interface LMViewController () { 14 | MPMediaPlaylist *currentPlaylist_; 15 | LMMediaPlayerView *playerView_; 16 | NSArray *musics_; 17 | NSArray *playlists_; 18 | } 19 | 20 | @end 21 | 22 | @implementation LMViewController 23 | 24 | - (id)initWithPlaylist:(MPMediaPlaylist *)playlist 25 | { 26 | self = [self initWithStyle:UITableViewStylePlain]; 27 | if (self) { 28 | currentPlaylist_ = playlist; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | - (void)dealloc 35 | { 36 | playerView_.delegate = nil; 37 | #if !__has_feature(objc_arc) 38 | [super dealloc]; 39 | [musics_ release]; 40 | #endif 41 | } 42 | 43 | - (void)viewDidLoad 44 | { 45 | [super viewDidLoad]; 46 | if (currentPlaylist_) { 47 | musics_ = [currentPlaylist_ items]; 48 | self.title = [currentPlaylist_ valueForProperty:MPMediaPlaylistPropertyName]; 49 | } 50 | else { 51 | MPMediaQuery *q = [[MPMediaQuery alloc] init]; 52 | musics_ = [q items]; 53 | #if !__has_feature(objc_arc) 54 | [q release]; 55 | #endif 56 | playlists_ = [[MPMediaQuery playlistsQuery] collections]; 57 | } 58 | playerView_ = [LMMediaPlayerView sharedPlayerView]; 59 | playerView_.delegate = self; 60 | [playerView_ setBluredUserInterface:YES visualEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; 61 | // [playerView_ setBluredUserInterface:NO visualEffect:nil]; 62 | } 63 | 64 | - (void)viewDidAppear:(BOOL)animated 65 | { 66 | [super viewDidAppear:animated]; 67 | [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 68 | [self becomeFirstResponder]; 69 | 70 | static dispatch_once_t onceToken; 71 | dispatch_once(&onceToken, ^{ 72 | NSURL *path = [NSURL URLWithString:@"https://github.com/0x0c/LMMediaPlayer/blob/master/sample.mp4?raw=true"]; 73 | LMMediaItem *item = [[LMMediaItem alloc] initWithInfo:@{LMMediaItemInfoURLKey:path, LMMediaItemInfoContentTypeKey:@(LMMediaItemContentTypeVideo)}]; 74 | item.title = @"sample.mp4"; 75 | [playerView_.mediaPlayer addMedia:item]; 76 | path = [[NSBundle mainBundle] URLForResource:@"IMG_2380" withExtension:@"MOV"]; 77 | item = [[LMMediaItem alloc] initWithInfo:@{LMMediaItemInfoURLKey:path, LMMediaItemInfoContentTypeKey:@(LMMediaItemContentTypeVideo)}]; 78 | item.title = @"IMG_2380.MOV"; 79 | [playerView_.mediaPlayer addMedia:item]; 80 | 81 | [playerView_.mediaPlayer play]; 82 | #if !__has_feature(objc_arc) 83 | [item release]; 84 | #endif 85 | }); 86 | } 87 | 88 | - (void)viewWillAppear:(BOOL)animated 89 | { 90 | [super viewWillAppear:animated]; 91 | 92 | playerView_.delegate = self; 93 | 94 | UIView *baseView = [[UIView alloc] initWithFrame:playerView_.frame]; 95 | baseView.backgroundColor = [UIColor blackColor]; 96 | [baseView addSubview:playerView_]; 97 | self.tableView.tableHeaderView = baseView; 98 | #if !__has_feature(objc_arc) 99 | [baseView release]; 100 | #endif 101 | } 102 | 103 | - (void)viewWillDisappear:(BOOL)animated 104 | { 105 | [super viewWillDisappear:animated]; 106 | 107 | [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; 108 | [self resignFirstResponder]; 109 | 110 | playerView_.delegate = self; 111 | } 112 | 113 | - (BOOL)shouldAutorotate 114 | { 115 | return NO; 116 | } 117 | 118 | #pragma mark - LMMediaPlayerViewDelegate 119 | 120 | - (BOOL)mediaPlayerViewWillStartPlaying:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media 121 | { 122 | return YES; 123 | } 124 | 125 | #pragma mark - Table view data source 126 | 127 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 128 | { 129 | // Return the number of sections. 130 | return currentPlaylist_ == nil ? 2 : 1; 131 | } 132 | 133 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 134 | { 135 | // Return the number of rows in the section. 136 | return section == 0 ? [musics_ count] : [playlists_ count]; 137 | } 138 | 139 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 140 | { 141 | static NSString *CellIdentifier = @"Cell"; 142 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 143 | 144 | // Configure the cell... 145 | if (cell == nil) { 146 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 147 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 148 | #if !__has_feature(objc_arc) 149 | [cell autorelease]; 150 | #endif 151 | } 152 | 153 | if (indexPath.section == 0) { 154 | cell.textLabel.text = [(MPMediaItem *)musics_[indexPath.row] valueForProperty:MPMediaItemPropertyTitle]; 155 | cell.detailTextLabel.text = [(MPMediaItem *)musics_[indexPath.row] valueForProperty:MPMediaItemPropertyArtist]; 156 | MPMediaItemArtwork *artwork = [musics_[indexPath.row] valueForProperty:MPMediaItemPropertyArtwork]; 157 | cell.imageView.image = [artwork imageWithSize:CGSizeMake(44, 44)]; 158 | } 159 | else if (indexPath.section == 1) { 160 | cell.textLabel.text = [(MPMediaPlaylist *)playlists_[indexPath.row] valueForProperty:MPMediaPlaylistPropertyName]; 161 | cell.detailTextLabel.text = @""; 162 | cell.imageView.image = nil; 163 | } 164 | 165 | return cell; 166 | } 167 | 168 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 169 | { 170 | NSString *title = nil; 171 | if (section == 0) { 172 | title = @"Songs"; 173 | } 174 | else { 175 | title = @"Playlists"; 176 | } 177 | 178 | return title; 179 | } 180 | 181 | #pragma mark - Table view delegate 182 | 183 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 184 | { 185 | if (indexPath.section == 0) { 186 | NSURL *url = [musics_[indexPath.row] valueForProperty:MPMediaItemPropertyAssetURL]; 187 | if (url.absoluteString.length) { 188 | NSNumber *type = [musics_[indexPath.row] valueForProperty:MPMediaItemPropertyMediaType]; 189 | LMMediaItem *item = [[LMMediaItem alloc] initWithMetaMedia:musics_[indexPath.row] contentType:([type integerValue] & MPMediaTypeMusicVideo) ? LMMediaItemContentTypeVideo : LMMediaItemContentTypeAudio]; 190 | [playerView_.mediaPlayer addMedia:item]; 191 | #if !__has_feature(objc_arc) 192 | [item release]; 193 | #endif 194 | } 195 | else { 196 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"DRM content" message:@"You cannot add this content because of DRM." preferredStyle:UIAlertControllerStyleAlert]; 197 | [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; 198 | [self presentViewController:alert animated:YES completion:nil]; 199 | } 200 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 201 | } 202 | else { 203 | LMViewController *viewController = [[LMViewController alloc] initWithPlaylist:playlists_[indexPath.row]]; 204 | [self.navigationController pushViewController:viewController animated:YES]; 205 | #if !__has_feature(objc_arc) 206 | [viewController release]; 207 | #endif 208 | } 209 | } 210 | 211 | #pragma mark - 212 | #pragma mark - remote control event 213 | - (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent 214 | { 215 | if (receivedEvent.type == UIEventTypeRemoteControl) { 216 | switch (receivedEvent.subtype) { 217 | case UIEventSubtypeRemoteControlPlay: 218 | case UIEventSubtypeRemoteControlPause: 219 | case UIEventSubtypeRemoteControlTogglePlayPause: { 220 | if ([playerView_.mediaPlayer playbackState] == LMMediaPlaybackStatePlaying) { 221 | [playerView_.mediaPlayer pause]; 222 | } 223 | else if ([playerView_.mediaPlayer playbackState] == LMMediaPlaybackStatePaused || [playerView_.mediaPlayer playbackState] == LMMediaPlaybackStateStopped) { 224 | [playerView_.mediaPlayer play]; 225 | } 226 | } break; 227 | case UIEventSubtypeRemoteControlPreviousTrack: { 228 | [playerView_.mediaPlayer playPreviousMedia]; 229 | } break; 230 | case UIEventSubtypeRemoteControlNextTrack: { 231 | [playerView_.mediaPlayer playNextMedia]; 232 | } break; 233 | default: 234 | break; 235 | } 236 | } 237 | } 238 | 239 | - (BOOL)canBecomeFirstResponder 240 | { 241 | return YES; 242 | } 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "LMAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([LMAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/LMMediaPlayer/sample.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Example/LMMediaPlayer/sample.mp4 -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | target 'LMMediaPlayer' do 2 | pod "LMMediaPlayer", :path => "../" 3 | end 4 | 5 | target 'Tests' do 6 | pod "LMMediaPlayer", :path => "../" 7 | 8 | pod 'Specta', '~> 0.2.1' 9 | pod 'Expecta' 10 | end 11 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (1.0.6) 3 | - LMMediaPlayer (0.6.1) 4 | - Specta (0.2.1) 5 | 6 | DEPENDENCIES: 7 | - Expecta 8 | - LMMediaPlayer (from `../`) 9 | - Specta (~> 0.2.1) 10 | 11 | SPEC REPOS: 12 | https://github.com/cocoapods/specs.git: 13 | - Expecta 14 | - Specta 15 | 16 | EXTERNAL SOURCES: 17 | LMMediaPlayer: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5 22 | LMMediaPlayer: c5e87d9a896cac8b8aa341f2937bc7a62e4fc6fe 23 | Specta: 15a276a6343867b426d5ed135d5aa4d04123a573 24 | 25 | PODFILE CHECKSUM: 497f067e7b5d5fcead81226d0b64d793f0687359 26 | 27 | COCOAPODS: 1.6.2 28 | -------------------------------------------------------------------------------- /Example/Tests/Tests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every test case source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | 9 | #define EXP_SHORTHAND 10 | #import 11 | #import 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayerTests.m 3 | // LMMediaPlayerTests 4 | // 5 | // Created by Akira Matsuda on 08/28/2014. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | SpecBegin(InitialSpecs) 10 | 11 | describe(@"these will fail", ^{ 12 | 13 | it(@"can do maths", ^{ 14 | expect(1).to.equal(2); 15 | }); 16 | 17 | it(@"can read", ^{ 18 | expect(@"number").to.equal(@"string"); 19 | }); 20 | 21 | it(@"will wait and fail", ^AsyncBlock { 22 | 23 | }); 24 | }); 25 | 26 | describe(@"these will pass", ^{ 27 | 28 | it(@"can do maths", ^{ 29 | expect(1).beLessThan(23); 30 | }); 31 | 32 | it(@"can read", ^{ 33 | expect(@"team").toNot.contain(@"I"); 34 | }); 35 | 36 | it(@"will wait and succeed", ^AsyncBlock { 37 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 38 | done(); 39 | }); 40 | }); 41 | }); 42 | 43 | SpecEnd 44 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gem "cocoapods" 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | activesupport (4.2.11.1) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | atomos (0.1.3) 11 | claide (1.0.2) 12 | cocoapods (1.6.2) 13 | activesupport (>= 4.0.2, < 5) 14 | claide (>= 1.0.2, < 2.0) 15 | cocoapods-core (= 1.6.2) 16 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 17 | cocoapods-downloader (>= 1.2.2, < 2.0) 18 | cocoapods-plugins (>= 1.0.0, < 2.0) 19 | cocoapods-search (>= 1.0.0, < 2.0) 20 | cocoapods-stats (>= 1.0.0, < 2.0) 21 | cocoapods-trunk (>= 1.3.1, < 2.0) 22 | cocoapods-try (>= 1.1.0, < 2.0) 23 | colored2 (~> 3.1) 24 | escape (~> 0.0.4) 25 | fourflusher (>= 2.2.0, < 3.0) 26 | gh_inspector (~> 1.0) 27 | molinillo (~> 0.6.6) 28 | nap (~> 1.0) 29 | ruby-macho (~> 1.4) 30 | xcodeproj (>= 1.8.1, < 2.0) 31 | cocoapods-core (1.6.2) 32 | activesupport (>= 4.0.2, < 6) 33 | fuzzy_match (~> 2.0.4) 34 | nap (~> 1.0) 35 | cocoapods-deintegrate (1.0.4) 36 | cocoapods-downloader (1.2.2) 37 | cocoapods-plugins (1.0.0) 38 | nap 39 | cocoapods-search (1.0.0) 40 | cocoapods-stats (1.1.0) 41 | cocoapods-trunk (1.3.1) 42 | nap (>= 0.8, < 2.0) 43 | netrc (~> 0.11) 44 | cocoapods-try (1.1.0) 45 | colored2 (3.1.2) 46 | concurrent-ruby (1.1.5) 47 | escape (0.0.4) 48 | fourflusher (2.2.0) 49 | fuzzy_match (2.0.4) 50 | gh_inspector (1.1.3) 51 | i18n (0.9.5) 52 | concurrent-ruby (~> 1.0) 53 | minitest (5.11.3) 54 | molinillo (0.6.6) 55 | nanaimo (0.2.6) 56 | nap (1.1.0) 57 | netrc (0.11.0) 58 | ruby-macho (1.4.0) 59 | thread_safe (0.3.6) 60 | tzinfo (1.2.5) 61 | thread_safe (~> 0.1) 62 | xcodeproj (1.9.0) 63 | CFPropertyList (>= 2.3.3, < 4.0) 64 | atomos (~> 0.1.3) 65 | claide (>= 1.0.2, < 2.0) 66 | colored2 (~> 3.1) 67 | nanaimo (~> 0.2.6) 68 | 69 | PLATFORMS 70 | ruby 71 | 72 | DEPENDENCIES 73 | cocoapods 74 | 75 | BUNDLED WITH 76 | 1.16.3 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Akira Matsuda 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LMMediaPlayer.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint LMMediaPlayer.podspec' to ensure this is a 3 | # valid spec and remove all comments before submitting the spec. 4 | # 5 | # Any lines starting with a # are optional, but encouraged 6 | # 7 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 8 | # 9 | 10 | Pod::Spec.new do |s| 11 | s.name = "LMMediaPlayer" 12 | s.version = "0.7.0" 13 | s.summary = "Video and audio player with replaceable UI component." 14 | s.homepage = "https://github.com/0x0c/LMMediaPlayer" 15 | 16 | s.license = 'MIT' 17 | s.author = { "Akira Matsuda" => "akira.matsuda@me.com" } 18 | s.source = { :git => "https://github.com/0x0c/LMMediaPlayer.git", :tag => s.version.to_s } 19 | 20 | s.platform = :ios, '10.0' 21 | s.requires_arc = true 22 | 23 | s.source_files = 'Pod/Classes/**/*.{h,m}' 24 | s.resources = ['Pod/Assets/LMMediaPlayerView.xib', 'Pod/Assets/LMMediaPlayerView.bundle'] 25 | 26 | s.public_header_files = 'Pod/Classes/**/*.h' 27 | s.frameworks = 'UIKit', 'AVFoundation', 'MediaPlayer' 28 | 29 | s.description = <<-DESC 30 | LMMediaPlayer is a video and audio player for iPhone with changeable user interface. 31 | 32 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/2.png) 33 | 34 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/1.png) 35 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/3.png) 36 | 37 | Requirements 38 | ==== 39 | 40 | - Runs on iOS 6.0 or later. 41 | - Must be complied with ARC. 42 | 43 | Intstallation 44 | === 45 | 46 | First, please add these frameworks. 47 | 48 | === 49 | #import 50 | #import 51 | === 52 | 53 | Second, add files which is contained "LMMediaPlayer" folder. 54 | 55 | That's it. 56 | 57 | If you want to play with fullscreen mode, please add "View controller-based status bar appearance" key and set value with "NO" at your Info.plist 58 | DESC 59 | end 60 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/fullscreen.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/fullscreen@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/pause.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/pause@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/play.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/play@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_all.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_all@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_all@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_none.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_none@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_none@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_one.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/repeat_one@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/repeat_one@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/shuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/shuffle.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/shuffle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/shuffle@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/unfullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/unfullscreen.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/unfullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/unfullscreen@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/unshuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/unshuffle.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/6/unshuffle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/6/unshuffle@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/fullscreen.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/fullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/fullscreen@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/pause.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/pause@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/play.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/play@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_all.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_all@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_all@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_none.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_none@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_none@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_one.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/repeat_one@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/repeat_one@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/shuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/shuffle.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/shuffle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/shuffle@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/unfullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/unfullscreen.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/unfullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/unfullscreen@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/unshuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/unshuffle.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.bundle/7/unshuffle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Assets/LMMediaPlayerView.bundle/7/unshuffle@2x.png -------------------------------------------------------------------------------- /Pod/Assets/LMMediaPlayerView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 54 | 64 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 111 | 125 | 134 | 144 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/Category/NSArray+LMMediaPlayerShuffle/NSArray+LMMediaPlayerShuffle.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Shuffle.h 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/22. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (LMMediaPlayerShuffle) 12 | 13 | - (NSArray *)lm_shuffledArray; 14 | 15 | @end 16 | 17 | @interface NSMutableArray (LMMediaPlayerShuffle) 18 | 19 | - (void)lm_shuffle; 20 | 21 | @end -------------------------------------------------------------------------------- /Pod/Classes/Category/NSArray+LMMediaPlayerShuffle/NSArray+LMMediaPlayerShuffle.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Shuffle.m 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/22. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "NSArray+LMMediaPlayerShuffle.h" 10 | 11 | @implementation NSArray (LMMediaPlayerShuffle) 12 | 13 | - (NSArray *)lm_shuffledArray 14 | { 15 | NSMutableArray *results = [NSMutableArray arrayWithArray:self]; 16 | NSUInteger i = [results count]; 17 | 18 | while (--i) { 19 | [results exchangeObjectAtIndex:i withObjectAtIndex:(NSUInteger)arc4random_uniform(i + 1.0)]; 20 | } 21 | 22 | return [NSArray arrayWithArray:results]; 23 | } 24 | 25 | @end 26 | 27 | @implementation NSMutableArray (LMMediaPlayerShuffle) 28 | 29 | - (void)lm_shuffle 30 | { 31 | NSUInteger i = [self count]; 32 | while (--i) { 33 | [self exchangeObjectAtIndex:i withObjectAtIndex:(NSUInteger)arc4random_uniform(i + 1.0)]; 34 | } 35 | } 36 | 37 | @end -------------------------------------------------------------------------------- /Pod/Classes/Hepler/LMMediaPlayerHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayerHelper.h 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 8/31/14. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #if !__has_feature(objc_arc) 12 | #define LM_AUTORELEASE(v) [v autorelease] 13 | #define LM_RETAIN(v) [v retain] 14 | #define LM_DEALLOC(v) [v dealloc] 15 | #define LM_RELEASE(v) [v release] 16 | #define LM_RELEASE_NIL(v) \ 17 | [v release]; \ 18 | v = nil; 19 | #else 20 | #define LM_AUTORELEASE(v) 21 | #define LM_RETAIN(v) 22 | #define LM_DEALLOC(v) 23 | #define LM_RELEASE(v) 24 | #define LM_RELEASE_NIL(v) 25 | #endif 26 | 27 | @interface LMMediaPlayerHelper : NSObject 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Pod/Classes/Hepler/LMMediaPlayerHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayerHelper.m 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 8/31/14. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaPlayerHelper.h" 10 | 11 | @implementation LMMediaPlayerHelper 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/LMProgressBarView/LMProgressBarView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TYMProgressBarView.h 3 | // TYMProgressBarView 4 | // 5 | // Created by Yiming Tang on 6/7/13. 6 | // Copyright (c) 2013 - 2015 Yiming Tang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Class name is renamed to avoid conflicts e.g someone is using any other version of the same class 13 | */ 14 | 15 | /** 16 | Progress bar similar to the one in iOS's launching screen. 17 | */ 18 | @interface LMProgressBarView : UIView 19 | 20 | ///--------------------------- 21 | ///@name Managing the Progress 22 | ///--------------------------- 23 | 24 | /** 25 | The current progress shown by the receiver. 26 | 27 | The current progress is represented by a floating-point value between `0.0` and `1.0`, inclusive, where `1.0` indicates 28 | the completion of the task. Values less than `0.0` and greater than `1.0` are pinned to those limits. 29 | 30 | The default value is `0.0`. 31 | */ 32 | @property (nonatomic) CGFloat progress; 33 | 34 | @property (nonatomic) CGFloat currentProgress; 35 | 36 | ///------------------------------------- 37 | /// @name Configuring the Appearance 38 | ///------------------------------------- 39 | 40 | /** 41 | The outer border width. 42 | 43 | The default is `2.0`. 44 | */ 45 | @property (nonatomic) CGFloat barBorderWidth UI_APPEARANCE_SELECTOR; 46 | 47 | /** 48 | The outer border color. 49 | 50 | @see defaultBarColor 51 | */ 52 | @property (nonatomic, strong) UIColor *barBorderColor UI_APPEARANCE_SELECTOR; 53 | 54 | /** 55 | The inner border width. 56 | 57 | The default is `0.0`. 58 | */ 59 | @property (nonatomic) CGFloat barInnerBorderWidth UI_APPEARANCE_SELECTOR; 60 | 61 | /** 62 | The inner border color. 63 | 64 | The default is nil. 65 | */ 66 | @property (nonatomic, strong) UIColor *barInnerBorderColor UI_APPEARANCE_SELECTOR; 67 | 68 | /** 69 | The inner padding. 70 | 71 | The default is `2.0`. 72 | */ 73 | @property (nonatomic) CGFloat barInnerPadding UI_APPEARANCE_SELECTOR; 74 | 75 | /** 76 | The fill color. 77 | 78 | @see defaultBarColor 79 | */ 80 | @property (nonatomic, strong) UIColor *barFillColor UI_APPEARANCE_SELECTOR; 81 | 82 | @property (nonatomic, strong) UIColor *barMinimumTrackFillColor UI_APPEARANCE_SELECTOR; 83 | /** 84 | The bar background color. 85 | 86 | The default is white. 87 | */ 88 | @property (nonatomic, strong) UIColor *barBackgroundColor UI_APPEARANCE_SELECTOR; 89 | 90 | /** 91 | It determines whether use rounded corners. 92 | 93 | The default is `YES`. 94 | 95 | @discussion We can't set `BOOL` type through the `UIAppearance` API, so we use `NSInteger` instead. 96 | */ 97 | @property (nonatomic) NSInteger usesRoundedCorners UI_APPEARANCE_SELECTOR; 98 | 99 | ///--------------- 100 | /// @name Defaults 101 | ///--------------- 102 | 103 | /** 104 | The default value of `barBorderColor` and `barFillColor`. 105 | */ 106 | + (UIColor *)defaultBarColor; 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Pod/Classes/LMProgressBarView/LMProgressBarView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TYMProgressBarView.m 3 | // TYMProgressBarView 4 | // 5 | // Created by Yiming Tang on 6/7/13. 6 | // Copyright (c) 2013 - 2015 Yiming Tang. All rights reserved. 7 | // 8 | 9 | #import "LMProgressBarView.h" 10 | 11 | void strokeRectInContext(CGContextRef context, CGRect rect, CGFloat lineWidth, CGFloat radius); 12 | void fillRectInContext(CGContextRef context, CGRect rect, CGFloat radius); 13 | void setRectPathInContext(CGContextRef context, CGRect rect, CGFloat radius); 14 | 15 | @implementation LMProgressBarView 16 | 17 | #pragma mark - Accessors 18 | 19 | @synthesize progress = _progress; 20 | @synthesize barBorderWidth = _barBorderWidth; 21 | @synthesize barBorderColor = _barBorderColor; 22 | @synthesize barInnerBorderWidth = _barInnerBorderWidth; 23 | @synthesize barInnerBorderColor = _barInnerBorderColor; 24 | @synthesize barInnerPadding = _barInnerPadding; 25 | @synthesize barFillColor = _barFillColor; 26 | @synthesize barBackgroundColor = _barBackgroundColor; 27 | @synthesize usesRoundedCorners = _usesRoundedCorners; 28 | 29 | - (void)setProgress:(CGFloat)newProgress 30 | { 31 | _progress = fmaxf(0.0, fminf(1.0, newProgress)); 32 | [self setNeedsDisplay]; 33 | } 34 | 35 | - (void)setCurrentProgress:(CGFloat)newProgress 36 | { 37 | _currentProgress = fmaxf(0.0, fminf(1.0, newProgress)); 38 | [self setNeedsDisplay]; 39 | } 40 | 41 | - (void)setBarBorderWidth:(CGFloat)barBorderWidth 42 | { 43 | _barBorderWidth = barBorderWidth; 44 | [self setNeedsDisplay]; 45 | } 46 | 47 | - (void)setBarBorderColor:(UIColor *)barBorderColor 48 | { 49 | _barBorderColor = barBorderColor; 50 | [self setNeedsDisplay]; 51 | } 52 | 53 | - (void)setBarInnerBorderWidth:(CGFloat)barInnerBorderWidth 54 | { 55 | _barInnerBorderWidth = barInnerBorderWidth; 56 | [self setNeedsDisplay]; 57 | } 58 | 59 | - (void)setBarInnerBorderColor:(UIColor *)barInnerBorderColor 60 | { 61 | _barInnerBorderColor = barInnerBorderColor; 62 | [self setNeedsDisplay]; 63 | } 64 | 65 | - (void)setBarInnerPadding:(CGFloat)barInnerPadding 66 | { 67 | _barInnerPadding = barInnerPadding; 68 | [self setNeedsDisplay]; 69 | } 70 | 71 | - (void)setBarFillColor:(UIColor *)barFillColor 72 | { 73 | _barFillColor = barFillColor; 74 | [self setNeedsDisplay]; 75 | } 76 | 77 | - (void)setBarMinimumTrackFillColor:(UIColor *)barMinimumTrackFillColor 78 | { 79 | _barMinimumTrackFillColor = barMinimumTrackFillColor; 80 | [self setNeedsDisplay]; 81 | } 82 | 83 | - (void)setBarBackgroundColor:(UIColor *)barBackgroundColor 84 | { 85 | _barBackgroundColor = barBackgroundColor; 86 | [self setNeedsDisplay]; 87 | } 88 | 89 | - (void)setUsesRoundedCorners:(NSInteger)usesRoundedCorners 90 | { 91 | _usesRoundedCorners = usesRoundedCorners; 92 | [self setNeedsDisplay]; 93 | } 94 | 95 | #pragma mark - Class Methods 96 | 97 | + (UIColor *)defaultBarColor 98 | { 99 | return [UIColor darkGrayColor]; 100 | } 101 | 102 | + (void)initialize 103 | { 104 | if (self == [LMProgressBarView class]) { 105 | LMProgressBarView *appearance = [LMProgressBarView appearance]; 106 | [appearance setUsesRoundedCorners:YES]; 107 | [appearance setProgress:0]; 108 | [appearance setBarBorderWidth:2.0]; 109 | [appearance setBarBorderColor:[self defaultBarColor]]; 110 | [appearance setBarInnerBorderWidth:0]; 111 | [appearance setBarInnerBorderColor:nil]; 112 | [appearance setBarInnerPadding:2.0]; 113 | [appearance setBarFillColor:[self defaultBarColor]]; 114 | [appearance setBarBackgroundColor:[UIColor whiteColor]]; 115 | [appearance setBarMinimumTrackFillColor:[UIColor whiteColor]]; 116 | } 117 | } 118 | 119 | #pragma mark - UIView 120 | 121 | - (id)initWithCoder:(NSCoder *)aDecoder 122 | { 123 | if ((self = [super initWithCoder:aDecoder])) { 124 | [self initialize]; 125 | } 126 | return self; 127 | } 128 | 129 | - (id)initWithFrame:(CGRect)aFrame 130 | { 131 | if ((self = [super initWithFrame:aFrame])) { 132 | [self initialize]; 133 | } 134 | return self; 135 | } 136 | 137 | - (void)drawRect:(CGRect)rect 138 | { 139 | CGContextRef context = UIGraphicsGetCurrentContext(); 140 | CGContextSaveGState(context); 141 | CGContextSetAllowsAntialiasing(context, TRUE); 142 | 143 | CGRect currentRect = rect; 144 | CGFloat radius = 0; 145 | CGFloat halfLineWidth = 0; 146 | 147 | // Background 148 | if (self.backgroundColor) { 149 | if (self.usesRoundedCorners) 150 | radius = currentRect.size.height / 2.0; 151 | 152 | [self.barBackgroundColor setFill]; 153 | fillRectInContext(context, currentRect, radius); 154 | } 155 | 156 | // Border 157 | if (self.barBorderColor && self.barBorderWidth > 0.0) { 158 | // Inset, because a stroke is centered on the path 159 | // See http://stackoverflow.com/questions/10557157/drawing-rounded-rect-in-core-graphics 160 | halfLineWidth = self.barBorderWidth / 2.0; 161 | currentRect = CGRectInset(currentRect, halfLineWidth, halfLineWidth); 162 | if (self.usesRoundedCorners) 163 | radius = currentRect.size.height / 2.0; 164 | 165 | [self.barBorderColor setStroke]; 166 | strokeRectInContext(context, currentRect, self.barBorderWidth, radius); 167 | 168 | currentRect = CGRectInset(currentRect, halfLineWidth, halfLineWidth); 169 | } 170 | 171 | // Padding 172 | currentRect = CGRectInset(currentRect, self.barInnerPadding, self.barInnerPadding); 173 | 174 | BOOL hasInnerBorder = NO; 175 | // Inner border 176 | if (self.barInnerBorderColor && self.barInnerBorderWidth > 0.0) { 177 | hasInnerBorder = YES; 178 | halfLineWidth = self.barInnerBorderWidth / 2.0; 179 | currentRect = CGRectInset(currentRect, halfLineWidth, halfLineWidth); 180 | if (self.usesRoundedCorners) 181 | radius = currentRect.size.height / 2.0; 182 | 183 | // progress 184 | currentRect.size.width *= self.progress; 185 | currentRect.size.width = fmaxf(currentRect.size.width, 2 * radius); 186 | 187 | [self.barInnerBorderColor setStroke]; 188 | strokeRectInContext(context, currentRect, self.barInnerBorderWidth, radius); 189 | 190 | currentRect = CGRectInset(currentRect, halfLineWidth, halfLineWidth); 191 | } 192 | 193 | // Fill 194 | if (self.barFillColor) { 195 | if (self.usesRoundedCorners) 196 | radius = currentRect.size.height / 2; 197 | 198 | // recalculate width 199 | if (!hasInnerBorder) { 200 | currentRect.size.width *= self.progress; 201 | currentRect.size.width = fmaxf(currentRect.size.width, 2 * radius); 202 | } 203 | 204 | [self.barFillColor setFill]; 205 | fillRectInContext(context, currentRect, radius); 206 | } 207 | 208 | if (self.barMinimumTrackFillColor) { 209 | 210 | CGRect progressFillRect = currentRect; 211 | progressFillRect.size.width = CGRectInset(rect, self.barInnerPadding, self.barInnerPadding).size.width; 212 | progressFillRect.size.width = CGRectInset(progressFillRect, halfLineWidth, halfLineWidth).size.width; 213 | progressFillRect.size.width = CGRectInset(progressFillRect, halfLineWidth, halfLineWidth).size.width; 214 | 215 | if (self.usesRoundedCorners) 216 | radius = progressFillRect.size.height / 2; 217 | 218 | progressFillRect.size.width *= self.currentProgress; 219 | progressFillRect.size.width = fmaxf(progressFillRect.size.width, 2 * radius); 220 | 221 | [self.barMinimumTrackFillColor setFill]; 222 | fillRectInContext(context, progressFillRect, radius); 223 | } 224 | 225 | // Restore the context 226 | CGContextRestoreGState(context); 227 | } 228 | 229 | #pragma mark - Private 230 | 231 | - (void)initialize 232 | { 233 | self.contentMode = UIViewContentModeRedraw; 234 | self.backgroundColor = [UIColor clearColor]; 235 | } 236 | 237 | @end 238 | 239 | #pragma mark - Drawing Functions 240 | 241 | void strokeRectInContext(CGContextRef context, CGRect rect, CGFloat lineWidth, CGFloat radius) 242 | { 243 | CGContextSetLineWidth(context, lineWidth); 244 | setRectPathInContext(context, rect, radius); 245 | CGContextStrokePath(context); 246 | } 247 | 248 | void fillRectInContext(CGContextRef context, CGRect rect, CGFloat radius) 249 | { 250 | setRectPathInContext(context, rect, radius); 251 | CGContextFillPath(context); 252 | } 253 | 254 | void setRectPathInContext(CGContextRef context, CGRect rect, CGFloat radius) 255 | { 256 | CGContextBeginPath(context); 257 | if (radius > 0.0) { 258 | CGContextMoveToPoint(context, CGRectGetMinX(rect), CGRectGetMidY(rect)); 259 | CGContextAddArcToPoint(context, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMidX(rect), CGRectGetMinY(rect), radius); 260 | CGContextAddArcToPoint(context, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMidY(rect), radius); 261 | CGContextAddArcToPoint(context, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMidX(rect), CGRectGetMaxY(rect), radius); 262 | CGContextAddArcToPoint(context, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMidY(rect), radius); 263 | } else { 264 | CGContextAddRect(context, rect); 265 | } 266 | CGContextClosePath(context); 267 | } 268 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItem/LMMediaItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItem.h 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/27. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern NSString *LMMediaItemInfoTitleKey; 13 | extern NSString *LMMediaItemInfoAlubumTitleKey; 14 | extern NSString *LMMediaItemInfoArtistKey; 15 | extern NSString *LMMediaItemInfoArtworkKey; 16 | extern NSString *LMMediaItemInfoURLKey; 17 | extern NSString *LMMediaItemInfoContentTypeKey; 18 | 19 | typedef NS_ENUM(NSUInteger, LMMediaItemContentType) { 20 | LMMediaItemContentTypeUnknown = -1, 21 | LMMediaItemContentTypeAudio = 0, 22 | LMMediaItemContentTypeVideo = 1 23 | }; 24 | 25 | @interface LMMediaItem : NSObject 26 | 27 | - (instancetype)initWithMetaMedia:(id)media contentType:(LMMediaItemContentType)type; 28 | - (instancetype)initWithInfo:(NSDictionary *)info; 29 | - (UIImage *)artworkImageWithSize:(CGSize)size; 30 | - (void)setArtworkImage:(UIImage *)image; 31 | - (BOOL)isVideo; 32 | 33 | @property (nonatomic, copy) NSString *title; 34 | @property (nonatomic, copy) NSString *albumTitle; 35 | @property (nonatomic, copy) NSString *artist; 36 | @property (nonatomic, copy) id metaMedia; 37 | @property (nonatomic, copy) NSURL *assetURL; 38 | @property (nonatomic, readonly) LMMediaItemContentType contentType; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItem/LMMediaItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItem.m 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/27. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaItem.h" 10 | #import "LMMediaPlayerHelper.h" 11 | #import 12 | #import 13 | 14 | @interface LMMediaItem () { 15 | id metaMedia_; 16 | NSString *title_; 17 | NSString *albumTitle_; 18 | NSString *artist_; 19 | UIImage *artworkImage_; 20 | NSURL *url_; 21 | } 22 | 23 | @end 24 | 25 | @implementation LMMediaItem 26 | 27 | NSString *LMMediaItemInfoTitleKey = @"LMMediaItemInfoTitleKey"; 28 | NSString *LMMediaItemInfoAlubumTitleKey = @"LMMediaItemInfoAlubumTitleKey"; 29 | NSString *LMMediaItemInfoArtistKey = @"LMMediaItemInfoArtistKey"; 30 | NSString *LMMediaItemInfoArtworkKey = @"LMMediaItemInfoArtworkKey"; 31 | NSString *LMMediaItemInfoURLKey = @"LMMediaItemInfoURLKey"; 32 | NSString *LMMediaItemInfoContentTypeKey = @"LMMediaItemInfoContentTypeKey"; 33 | 34 | @synthesize title = title_; 35 | @synthesize albumTitle = albumTitle_; 36 | @synthesize artist = artist_; 37 | @synthesize assetURL = url_; 38 | 39 | - (void)dealloc 40 | { 41 | LM_RELEASE(title_); 42 | LM_RELEASE(albumTitle_); 43 | LM_RELEASE(artist_); 44 | LM_RELEASE(artworkImage_); 45 | LM_RELEASE(url_); 46 | LM_DEALLOC(super); 47 | } 48 | 49 | - (instancetype)initWithMetaMedia:(id)media contentType:(LMMediaItemContentType)type 50 | { 51 | self = [super init]; 52 | if (self) { 53 | metaMedia_ = media; 54 | _contentType = type; 55 | } 56 | 57 | return self; 58 | } 59 | 60 | - (instancetype)initWithInfo:(NSDictionary *)info 61 | { 62 | self = [super init]; 63 | if (self) { 64 | title_ = ([info[LMMediaItemInfoTitleKey] isKindOfClass:[NSString class]] ? [info[LMMediaItemInfoTitleKey] copy] : nil); 65 | albumTitle_ = ([info[LMMediaItemInfoAlubumTitleKey] isKindOfClass:[NSString class]] ? [info[LMMediaItemInfoAlubumTitleKey] copy] : nil); 66 | artist_ = ([info[LMMediaItemInfoArtistKey] isKindOfClass:[NSString class]] ? [info[LMMediaItemInfoArtistKey] copy] : nil); 67 | artworkImage_ = ([info[LMMediaItemInfoArtworkKey] isKindOfClass:[UIImage class]] ? [info[LMMediaItemInfoArtworkKey] copy] : nil); 68 | url_ = ([info[LMMediaItemInfoURLKey] isKindOfClass:[NSURL class]] ? [info[LMMediaItemInfoURLKey] copy] : nil); 69 | _contentType = (LMMediaItemContentType)([info[LMMediaItemInfoContentTypeKey] isKindOfClass:[NSNumber class]] ? [info[LMMediaItemInfoContentTypeKey] integerValue] : -1); 70 | } 71 | 72 | return self; 73 | } 74 | 75 | - (instancetype)initWithCoder:(NSCoder *)coder 76 | { 77 | self = [super init]; 78 | if (self) { 79 | title_ = [coder decodeObjectForKey:LMMediaItemInfoTitleKey]; 80 | albumTitle_ = [coder decodeObjectForKey:LMMediaItemInfoAlubumTitleKey]; 81 | artist_ = [coder decodeObjectForKey:LMMediaItemInfoArtistKey]; 82 | artworkImage_ = [coder decodeObjectForKey:LMMediaItemInfoArtworkKey]; 83 | url_ = [coder decodeObjectForKey:LMMediaItemInfoURLKey]; 84 | _contentType = (LMMediaItemContentType)[[coder decodeObjectForKey:LMMediaItemInfoContentTypeKey] integerValue]; 85 | } 86 | 87 | return self; 88 | } 89 | 90 | - (void)encodeWithCoder:(NSCoder *)coder 91 | { 92 | [coder encodeObject:title_ forKey:LMMediaItemInfoTitleKey]; 93 | [coder encodeObject:albumTitle_ forKey:LMMediaItemInfoAlubumTitleKey]; 94 | [coder encodeObject:artist_ forKey:LMMediaItemInfoArtistKey]; 95 | [coder encodeObject:artworkImage_ forKey:LMMediaItemInfoArtworkKey]; 96 | [coder encodeObject:url_ forKey:LMMediaItemInfoURLKey]; 97 | [coder encodeObject:[NSNumber numberWithInteger:_contentType] forKey:LMMediaItemInfoContentTypeKey]; 98 | } 99 | 100 | - (id)copyWithZone:(NSZone *)zone 101 | { 102 | NSMutableDictionary *newInfo = [NSMutableDictionary new]; 103 | if (self.title) { 104 | NSString *newString = [self.title copy]; 105 | LM_AUTORELEASE(newString); 106 | newInfo[LMMediaItemInfoTitleKey] = newString ?: [NSNull null]; 107 | } 108 | if (self.albumTitle) { 109 | NSString *newString = [self.albumTitle copy]; 110 | LM_AUTORELEASE(newString); 111 | newInfo[LMMediaItemInfoAlubumTitleKey] = newString ?: [NSNull null]; 112 | } 113 | if (self.artist) { 114 | NSString *newString = [self.artist copy]; 115 | LM_AUTORELEASE(newString); 116 | newInfo[LMMediaItemInfoArtistKey] = newString ?: [NSNull null]; 117 | } 118 | if (artworkImage_) { 119 | UIImage *newImage = [artworkImage_ copy]; 120 | LM_AUTORELEASE(newImage); 121 | newInfo[LMMediaItemInfoArtworkKey] = newImage ?: [NSNull null]; 122 | } 123 | if (self.assetURL) { 124 | NSURL *newURL = [self.assetURL copy]; 125 | LM_AUTORELEASE(newURL); 126 | newInfo[LMMediaItemInfoURLKey] = newURL ?: [NSNull null]; 127 | } 128 | newInfo[LMMediaItemInfoContentTypeKey] = [NSNumber numberWithInteger:_contentType]; 129 | 130 | LMMediaItem *newObject = [[[self class] allocWithZone:zone] initWithInfo:newInfo]; 131 | LM_RELEASE(newInfo); 132 | 133 | return newObject; 134 | } 135 | 136 | - (id)valueWithProperty:(NSString *)property cache:(id)cache 137 | { 138 | id returnValue = nil; 139 | if ([metaMedia_ isKindOfClass:[MPMediaItem class]]) { 140 | returnValue = cache = [metaMedia_ valueForProperty:property]; 141 | } 142 | 143 | return returnValue; 144 | } 145 | 146 | - (NSString *)title 147 | { 148 | return title_ ?: [self valueWithProperty:MPMediaItemPropertyTitle cache:title_]; 149 | } 150 | 151 | - (NSString *)albumTitle 152 | { 153 | return albumTitle_ ?: [self valueWithProperty:MPMediaItemPropertyAlbumTitle cache:albumTitle_]; 154 | } 155 | 156 | - (NSString *)artist 157 | { 158 | return artist_ ?: [self valueWithProperty:MPMediaItemPropertyArtist cache:artist_]; 159 | } 160 | 161 | - (UIImage *)artworkImageWithSize:(CGSize)size 162 | { 163 | UIImage * (^f)(id) = ^UIImage *(id metaMedia) 164 | { 165 | UIImage *image = nil; 166 | if ([metaMedia isKindOfClass:[MPMediaItem class]]) { 167 | self->artworkImage_ = image = [[self->metaMedia_ valueForProperty:MPMediaItemPropertyArtwork] imageWithSize:size]; 168 | } 169 | 170 | return image; 171 | }; 172 | 173 | return artworkImage_ ?: f(metaMedia_); 174 | } 175 | 176 | - (void)setArtworkImage:(UIImage *)image 177 | { 178 | artworkImage_ = [image copy]; 179 | } 180 | 181 | - (NSURL *)assetURL 182 | { 183 | return url_ ?: [self valueWithProperty:MPMediaItemPropertyAssetURL cache:url_]; 184 | } 185 | 186 | - (id)metaMedia 187 | { 188 | return metaMedia_; 189 | } 190 | 191 | - (BOOL)isVideo 192 | { 193 | return _contentType == LMMediaItemContentTypeVideo; 194 | } 195 | 196 | - (NSString *)description 197 | { 198 | return [@{ @"title" : title_ ?: @"nil", 199 | @"album" : albumTitle_ ?: @"nil", 200 | @"artist" : artist_ ?: @"nil", 201 | @"url" : url_ ?: @"nil", 202 | @"artwork" : artworkImage_ ?: @"nil", 203 | @"content type" : _contentType == LMMediaItemContentTypeAudio ? @"LMMediaItemContentTypeAudio" : @"LMMediaItemContentTypeVideo", 204 | @"meta media" : metaMedia_ ?: @"nil" } description]; 205 | } 206 | 207 | @end 208 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItemQueueManager/LMMediaItemQueueManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItemArchiver.h 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/31. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaItem.h" 10 | #import 11 | 12 | @interface LMMediaItemQueueManager : NSObject 13 | 14 | + (NSArray *)queueList; 15 | + (void)removeQueueWithKey:(NSString *)key; 16 | + (void)saveQueueWithKey:(NSString *)key queue:(NSArray *)queue; 17 | + (NSArray *)loadQueueWithKey:(NSString *)key; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItemQueueManager/LMMediaItemQueueManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItemArchiver.m 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/31. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaItemQueueManager.h" 10 | #import "LMMediaPlayerHelper.h" 11 | 12 | static NSString *const kLMMediaItemQueueManagerQueueList = @"kLMMediaItemQueueManagerQueueList"; 13 | 14 | @implementation LMMediaItemQueueManager 15 | 16 | + (NSArray *)queueList 17 | { 18 | return [[NSUserDefaults standardUserDefaults] arrayForKey:kLMMediaItemQueueManagerQueueList]; 19 | } 20 | 21 | + (void)removeQueueWithKey:(NSString *)key 22 | { 23 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; 24 | NSMutableArray *keys = [NSMutableArray arrayWithArray:[LMMediaItemQueueManager queueList]]; 25 | for (NSString *k in keys) { 26 | if ([k isEqualToString:key]) { 27 | [keys removeObject:k]; 28 | break; 29 | } 30 | } 31 | 32 | [[NSUserDefaults standardUserDefaults] setObject:keys forKey:kLMMediaItemQueueManagerQueueList]; 33 | } 34 | 35 | + (void)saveQueueWithKey:(NSString *)key queue:(NSArray *)queue 36 | { 37 | NSMutableArray *saveArray = [NSMutableArray new]; 38 | LM_AUTORELEASE(saveArray); 39 | for (LMMediaItem *item in queue) { 40 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:item]; 41 | [saveArray addObject:data]; 42 | } 43 | [[NSUserDefaults standardUserDefaults] setObject:saveArray forKey:key]; 44 | 45 | NSMutableArray *keys = [NSMutableArray arrayWithArray:[LMMediaItemQueueManager queueList]]; 46 | [keys addObject:key]; 47 | [[NSUserDefaults standardUserDefaults] setObject:keys forKey:kLMMediaItemQueueManagerQueueList]; 48 | } 49 | 50 | + (NSArray *)loadQueueWithKey:(NSString *)key 51 | { 52 | NSMutableArray *result = [NSMutableArray new]; 53 | LM_AUTORELEASE(result); 54 | NSArray *array = [[NSUserDefaults standardUserDefaults] arrayForKey:key]; 55 | for (id d in array) { 56 | LMMediaItem *item = [NSKeyedUnarchiver unarchiveObjectWithData:d]; 57 | [result addObject:item]; 58 | } 59 | 60 | NSArray *newArray = [result copy]; 61 | LM_AUTORELEASE(newArray); 62 | return newArray; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItemStreamingCache/LMMediaItemStreamingCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItemCache.h 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 10/13/14. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LMMediaItemStreamingCache : NSObject 13 | 14 | + (instancetype)sharedCache; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaItemStreamingCache/LMMediaItemStreamingCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaItemCache.m 3 | // LMMediaPlayer 4 | // 5 | // Created by Akira Matsuda on 10/13/14. 6 | // Copyright (c) 2014 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaItemStreamingCache.h" 10 | #import 11 | 12 | @interface LMMediaItemStreamingCache () 13 | 14 | @end 15 | 16 | @implementation LMMediaItemStreamingCache 17 | 18 | + (instancetype)sharedCache 19 | { 20 | static id sharedInstance; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | sharedInstance = [[[self class] alloc] init]; 24 | }); 25 | 26 | return sharedInstance; 27 | } 28 | 29 | - (BOOL)isCacheAvailable:(NSString *)key 30 | { 31 | return NO; 32 | } 33 | 34 | #pragma mark - AVAssetResourceLoaderDelegate 35 | 36 | - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest 37 | { 38 | return YES; 39 | } 40 | 41 | - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest 42 | { 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaPlayer/LMMediaPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayer.h 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/10. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaItem.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | @class LMMediaPlayer; 16 | 17 | typedef NS_ENUM(NSUInteger, LMMediaPlaybackState) { 18 | LMMediaPlaybackStateStopped, 19 | LMMediaPlaybackStatePlaying, 20 | LMMediaPlaybackStatePaused, 21 | LMMediaPlaybackStateLoading, 22 | }; 23 | 24 | typedef NS_ENUM(NSUInteger, LMMediaRepeatMode) { 25 | LMMediaRepeatModeDefault, 26 | LMMediaRepeatModeOne, 27 | LMMediaRepeatModeAll, 28 | LMMediaRepeatModeNone = LMMediaRepeatModeDefault 29 | }; 30 | 31 | extern NSString *const LMMediaPlayerPauseNotification; 32 | extern NSString *const LMMediaPlayerStopNotification; 33 | 34 | @protocol LMMediaPlayerDelegate 35 | 36 | @required 37 | - (BOOL)mediaPlayerWillStartPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media; 38 | 39 | @optional 40 | - (void)mediaPlayerWillChangeState:(LMMediaPlaybackState)state; 41 | - (void)mediaPlayerDidStartPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media; 42 | - (void)mediaPlayerDidFinishPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media; 43 | - (void)mediaPlayerDidStop:(LMMediaPlayer *)player media:(LMMediaItem *)media; 44 | - (void)mediaPlayerDidChangeCurrentTime:(LMMediaPlayer *)player; 45 | - (void)mediaPlayerDidChangeRepeatMode:(LMMediaRepeatMode)mode player:(LMMediaPlayer *)player; 46 | - (void)mediaPlayerDidChangeShuffleMode:(BOOL)enabled player:(LMMediaPlayer *)player; 47 | - (void)mediaPlayerDidUpdateStreamingProgress:(float)progress player:(LMMediaPlayer *)player media:(LMMediaItem *)media; 48 | - (void)mediaPlayerDidFailedWithError:(NSError *)error player:(LMMediaPlayer *)player media:(LMMediaItem *)media; 49 | - (void)mediaPlayerWillStartLoading:(LMMediaPlayer *)player media:(LMMediaItem *)media; 50 | - (void)mediaPlayerDidEndLoading:(LMMediaPlayer *)player media:(LMMediaItem *)media; 51 | 52 | @end 53 | 54 | @interface LMMediaPlayer : NSObject 55 | 56 | @property (nonatomic, assign) id delegate; 57 | @property (nonatomic, readonly) LMMediaItem *nowPlayingItem; 58 | @property (nonatomic, readonly) LMMediaPlaybackState playbackState; 59 | @property (nonatomic, assign) LMMediaRepeatMode repeatMode; 60 | @property (nonatomic, readonly) BOOL shuffleMode; 61 | @property (nonatomic, readonly) NSInteger index; 62 | @property (nonatomic, readonly) NSString *currentAudioSessionCategory; 63 | 64 | + (instancetype)sharedPlayer; 65 | - (AVPlayer *)corePlayer; 66 | - (void)pauseOtherPlayer; 67 | - (void)stopOtherPlayer; 68 | - (void)addMedia:(LMMediaItem *)media; 69 | - (void)removeMediaAtIndex:(NSUInteger)index; 70 | - (void)replaceMediaAtIndex:(LMMediaItem *)media index:(NSInteger)index; 71 | - (void)removeAllMediaInQueue; 72 | - (void)setQueue:(NSArray *)queue; 73 | - (void)playMedia:(LMMediaItem *)media; 74 | - (void)play; 75 | - (void)playAtIndex:(NSInteger)index; 76 | - (void)stop; 77 | - (void)pause; 78 | - (void)playNextMedia; 79 | - (void)playPreviousMedia; 80 | - (NSArray *)queue; 81 | - (NSUInteger)numberOfQueue; 82 | - (NSTimeInterval)currentPlaybackTime; 83 | - (NSTimeInterval)currentPlaybackDuration; 84 | - (void)seekTo:(NSTimeInterval)time; 85 | - (void)setShuffleEnabled:(BOOL)enabled; 86 | - (UIImage *)thumbnailAtTime:(CGFloat)time; 87 | - (UIImage *)representativeThumbnail; 88 | - (NSError *)setAudioSessionCategory:(NSString *)category; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayer/LMMediaPlayer/LMMediaPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayer.m 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/10. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaPlayer.h" 10 | #import "LMMediaPlayerHelper.h" 11 | #import "NSArray+LMMediaPlayerShuffle.h" 12 | #import 13 | 14 | NSString *const LMMediaPlayerPauseNotification = @"LMMediaPlayerPauseNotification"; 15 | NSString *const LMMediaPlayerStopNotification = @"LMMediaPlayerStopNotification"; 16 | 17 | NSString *const kLMLoadedTimeRanges = @"loadedTimeRanges"; 18 | NSString *const kLMTracks = @"tracks"; 19 | NSString *const kLMPlayable = @"playable"; 20 | 21 | static void *AudioControllerBufferingObservationContext = &AudioControllerBufferingObservationContext; 22 | 23 | @interface LMMediaPlayer () { 24 | NSMutableArray *queue_; 25 | 26 | LMMediaPlaybackState playbackState_; 27 | AVPlayer *player_; 28 | id playerObserver_; 29 | } 30 | 31 | @property (nonatomic, strong) NSMutableArray *currentQueue; 32 | 33 | @end 34 | 35 | @implementation LMMediaPlayer 36 | 37 | @synthesize playbackState = playbackState_; 38 | 39 | static LMMediaPlayer *sharedPlayer; 40 | 41 | + (instancetype)sharedPlayer 42 | { 43 | static dispatch_once_t onceToken; 44 | dispatch_once(&onceToken, ^{ 45 | sharedPlayer = [[self class] new]; 46 | }); 47 | 48 | return sharedPlayer; 49 | } 50 | 51 | - (instancetype)init 52 | { 53 | self = [super init]; 54 | if (self) { 55 | player_ = [AVPlayer new]; 56 | queue_ = [NSMutableArray new]; 57 | self.currentQueue = queue_; 58 | _repeatMode = LMMediaRepeatModeDefault; 59 | _shuffleMode = YES; 60 | _currentAudioSessionCategory = AVAudioSessionCategoryPlayback; 61 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 62 | [notificationCenter addObserver:self selector:@selector(pause) name:LMMediaPlayerPauseNotification object:nil]; 63 | [notificationCenter addObserver:self selector:@selector(stop) name:LMMediaPlayerStopNotification object:nil]; 64 | } 65 | 66 | return self; 67 | } 68 | 69 | - (void)dealloc 70 | { 71 | self.delegate = nil; 72 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 73 | [notificationCenter removeObserver:self name:LMMediaPlayerPauseNotification object:nil]; 74 | [notificationCenter removeObserver:self name:LMMediaPlayerStopNotification object:nil]; 75 | [notificationCenter removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 76 | 77 | [self removeLMPlayerItemObservers]; 78 | 79 | LM_RELEASE(player_); 80 | LM_RELEASE(queue_); 81 | LM_RELEASE(_currentQueue); 82 | LM_DEALLOC(super); 83 | } 84 | 85 | - (void)removeLMPlayerItemObservers 86 | { 87 | @try { 88 | [self.corePlayer.currentItem removeObserver:self forKeyPath:kLMLoadedTimeRanges]; 89 | } @catch (NSException *exception) { 90 | } 91 | } 92 | #pragma mark - 93 | 94 | - (AVPlayer *)corePlayer 95 | { 96 | return player_; 97 | } 98 | 99 | - (void)pauseOtherPlayer 100 | { 101 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 102 | [notificationCenter removeObserver:self name:LMMediaPlayerPauseNotification object:nil]; 103 | [notificationCenter postNotificationName:LMMediaPlayerPauseNotification object:nil]; 104 | [notificationCenter addObserver:self selector:@selector(pause) name:LMMediaPlayerPauseNotification object:nil]; 105 | } 106 | 107 | - (void)stopOtherPlayer 108 | { 109 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 110 | [notificationCenter removeObserver:self name:LMMediaPlayerStopNotification object:nil]; 111 | [notificationCenter postNotificationName:LMMediaPlayerStopNotification object:nil]; 112 | [notificationCenter addObserver:self selector:@selector(stop) name:LMMediaPlayerStopNotification object:nil]; 113 | } 114 | 115 | - (void)playerItemDidReachEnd:(NSNotification *)notification 116 | { 117 | AVPlayerItem *item = notification.object; 118 | if (player_.currentItem == item) { 119 | [self playNextMedia]; 120 | } 121 | } 122 | 123 | - (void)addMedia:(LMMediaItem *)media 124 | { 125 | [self.currentQueue addObject:media]; 126 | } 127 | 128 | - (void)removeMediaAtIndex:(NSUInteger)index 129 | { 130 | LMMediaItem *item = _currentQueue[index]; 131 | if (item == _nowPlayingItem) { 132 | _nowPlayingItem = nil; 133 | [self playNextMedia]; 134 | } 135 | [self.currentQueue removeObjectAtIndex:index]; 136 | } 137 | 138 | - (void)replaceMediaAtIndex:(LMMediaItem *)media index:(NSInteger)index 139 | { 140 | LMMediaItem *item = _currentQueue[index]; 141 | if (item == _nowPlayingItem) { 142 | _nowPlayingItem = nil; 143 | } 144 | [self.currentQueue replaceObjectAtIndex:index withObject:media]; 145 | } 146 | 147 | - (void)removeAllMediaInQueue 148 | { 149 | _nowPlayingItem = nil; 150 | [self stop]; 151 | [self.currentQueue removeAllObjects]; 152 | } 153 | 154 | - (void)setQueue:(NSArray *)queue 155 | { 156 | for (LMMediaItem *item in queue) { 157 | [queue_ addObject:item]; 158 | } 159 | self.currentQueue = queue_; 160 | } 161 | 162 | - (void)updateLockScreenInfo 163 | { 164 | NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init]; 165 | [songInfo setObject:[_nowPlayingItem title] ?: @"" forKey:MPMediaItemPropertyTitle]; 166 | [songInfo setObject:[_nowPlayingItem albumTitle] ?: @"" forKey:MPMediaItemPropertyAlbumTitle]; 167 | [songInfo setObject:[_nowPlayingItem artist] ?: @"" forKey:MPMediaItemPropertyArtist]; 168 | [songInfo setObject:@([self currentPlaybackTime]) forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; 169 | [songInfo setObject:@([self currentPlaybackDuration]) forKey:MPMediaItemPropertyPlaybackDuration]; 170 | UIImage *artworkImage = [_nowPlayingItem artworkImageWithSize:CGSizeMake(320, 320)]; 171 | if (artworkImage) { 172 | MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artworkImage.size 173 | requestHandler:^UIImage *_Nonnull(CGSize size) { 174 | return artworkImage; 175 | }]; 176 | [songInfo setObject:artwork forKey:MPMediaItemPropertyArtwork]; 177 | LM_AUTORELEASE(artwork); 178 | } 179 | [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo]; 180 | LM_AUTORELEASE(songInfo); 181 | } 182 | 183 | - (void)playMedia:(LMMediaItem *)media 184 | { 185 | //Safely remove any previously added observer before adding new one 186 | [self removeLMPlayerItemObservers]; 187 | [self stop]; 188 | [self.corePlayer.currentItem.asset cancelLoading]; 189 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 190 | if ([self.delegate respondsToSelector:@selector(mediaPlayerWillStartPlaying:media:)]) { 191 | if ([self.delegate mediaPlayerWillStartPlaying:self media:media]) { 192 | if (media != nil) { 193 | NSURL *url = [media assetURL]; 194 | _nowPlayingItem = media; 195 | [player_ removeTimeObserver:playerObserver_]; 196 | 197 | [self setCurrentState:LMMediaPlaybackStateLoading]; 198 | 199 | if ([self.delegate respondsToSelector:@selector(mediaPlayerWillStartLoading:media:)]) { 200 | [self.delegate mediaPlayerWillStartLoading:self media:media]; 201 | } 202 | AVURLAsset *urlAsset = [AVURLAsset assetWithURL:url]; 203 | [urlAsset loadValuesAsynchronouslyForKeys:@[ kLMTracks, kLMPlayable ] 204 | completionHandler:^{ 205 | dispatch_async(dispatch_get_main_queue(), ^{ 206 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidEndLoading:media:)]) { 207 | [self.delegate mediaPlayerDidEndLoading:self media:media]; 208 | } 209 | 210 | NSError *error = nil; 211 | 212 | AVKeyValueStatus status = [urlAsset statusOfValueForKey:kLMTracks error:&error]; 213 | if (status == AVKeyValueStatusLoaded) { 214 | 215 | [self removeLMPlayerItemObservers]; 216 | AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset]; 217 | [self->player_ replaceCurrentItemWithPlayerItem:item]; 218 | [self.corePlayer.currentItem addObserver:self forKeyPath:kLMLoadedTimeRanges options:NSKeyValueObservingOptionNew context:AudioControllerBufferingObservationContext]; 219 | 220 | [self play]; 221 | 222 | } else { 223 | NSLog(@"unable to load asset current status: %zd error: %@", status, self.corePlayer.currentItem.error); 224 | [self setCurrentState:LMMediaPlaybackStateStopped]; 225 | [self removeLMPlayerItemObservers]; 226 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidFailedWithError:player:media:)]) { 227 | [self.delegate mediaPlayerDidFailedWithError:error player:self media:media]; 228 | } 229 | } 230 | }); 231 | }]; 232 | 233 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidStartPlaying:media:)]) { 234 | [self.delegate mediaPlayerDidStartPlaying:self media:media]; 235 | } 236 | player_.usesExternalPlaybackWhileExternalScreenIsActive = YES; 237 | __weak LMMediaPlayer *weakSelf = self; 238 | playerObserver_ = [player_ addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) 239 | queue:dispatch_get_main_queue() 240 | usingBlock:^(CMTime time) { 241 | __strong LMMediaPlayer* strongSelf = weakSelf; 242 | if(strongSelf) { 243 | if ([strongSelf.delegate respondsToSelector:@selector(mediaPlayerDidChangeCurrentTime:)]) { 244 | [strongSelf.delegate mediaPlayerDidChangeCurrentTime:strongSelf]; 245 | } 246 | } 247 | }]; 248 | } 249 | } 250 | } 251 | } 252 | 253 | - (void)play 254 | { 255 | if (playbackState_ == LMMediaPlaybackStateStopped) { 256 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 257 | [player_ seekToTime:CMTimeMake(0, 1)]; 258 | } 259 | if (_nowPlayingItem == nil) { 260 | [self playMedia:self.currentQueue.firstObject]; 261 | } else { 262 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 263 | [player_ play]; 264 | } 265 | 266 | [self setCurrentState:LMMediaPlaybackStatePlaying]; 267 | } 268 | 269 | - (void)playAtIndex:(NSInteger)index 270 | { 271 | _index = MAX(0, MIN(index, self.currentQueue.count - 1)); 272 | [self playMedia:self.currentQueue[_index]]; 273 | } 274 | 275 | - (void)stop 276 | { 277 | [player_ pause]; 278 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 279 | [self setCurrentState:LMMediaPlaybackStateStopped]; 280 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidStop:media:)]) { 281 | [self.delegate mediaPlayerDidStop:self media:_nowPlayingItem]; 282 | } 283 | _nowPlayingItem = nil; 284 | } 285 | 286 | - (void)pause 287 | { 288 | [player_ pause]; 289 | [self setCurrentState:LMMediaPlaybackStatePaused]; 290 | } 291 | 292 | - (void)playNextMedia 293 | { 294 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidFinishPlaying:media:)]) { 295 | [self.delegate mediaPlayerDidFinishPlaying:self media:_nowPlayingItem]; 296 | } 297 | if (self.currentQueue.count) { 298 | if (_repeatMode == LMMediaRepeatModeDefault) { 299 | if (_index >= self.currentQueue.count - 1) { 300 | _index = 0; 301 | [self stop]; 302 | } else { 303 | _index++; 304 | [self playMedia:self.currentQueue[_index]]; 305 | } 306 | } else if (_repeatMode == LMMediaRepeatModeAll) { 307 | if (_index >= self.currentQueue.count - 1) { 308 | _index = 0; 309 | } else { 310 | _index++; 311 | } 312 | [self playMedia:self.currentQueue[_index]]; 313 | } else { 314 | [self playMedia:self.nowPlayingItem]; 315 | } 316 | } else if (_repeatMode == LMMediaRepeatModeOne || _repeatMode == LMMediaRepeatModeAll) { 317 | [self playMedia:self.nowPlayingItem]; 318 | } 319 | } 320 | 321 | - (void)playPreviousMedia 322 | { 323 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidFinishPlaying:media:)]) { 324 | [self.delegate mediaPlayerDidFinishPlaying:self media:_nowPlayingItem]; 325 | } 326 | if (self.currentQueue.count) { 327 | if (_repeatMode == LMMediaRepeatModeDefault) { 328 | if (_index - 1 < 0) { 329 | _index = 0; 330 | [self stop]; 331 | } else { 332 | _index--; 333 | [self playMedia:self.currentQueue[_index]]; 334 | } 335 | } else if (_repeatMode == LMMediaRepeatModeAll) { 336 | if (_index - 1 < 0) { 337 | _index = self.currentQueue.count - 1; 338 | } else { 339 | _index--; 340 | } 341 | [self playMedia:self.currentQueue[_index]]; 342 | } else { 343 | [self playMedia:self.currentQueue[_index]]; 344 | } 345 | } else if (_repeatMode == LMMediaRepeatModeOne || _repeatMode == LMMediaRepeatModeAll) { 346 | [self playMedia:self.nowPlayingItem]; 347 | } 348 | } 349 | 350 | - (NSArray *)queue 351 | { 352 | NSArray *newArray = [self.currentQueue copy]; 353 | LM_AUTORELEASE(newArray); 354 | return newArray; 355 | } 356 | 357 | - (NSUInteger)numberOfQueue 358 | { 359 | return self.currentQueue.count; 360 | } 361 | 362 | - (NSTimeInterval)currentPlaybackTime 363 | { 364 | return player_.currentTime.value == 0 ? 0 : player_.currentTime.value / player_.currentTime.timescale; 365 | } 366 | 367 | - (NSTimeInterval)currentPlaybackDuration 368 | { 369 | return CMTimeGetSeconds([[player_.currentItem asset] duration]); 370 | } 371 | 372 | - (void)seekTo:(NSTimeInterval)time 373 | { 374 | [player_ seekToTime:CMTimeMake(time, 1)]; 375 | } 376 | 377 | - (void)setShuffleEnabled:(BOOL)enabled 378 | { 379 | _shuffleMode = enabled; 380 | if ([self numberOfQueue] > 0 && _shuffleMode) { 381 | NSMutableArray *newArray = [[self.currentQueue lm_shuffledArray] mutableCopy]; 382 | self.currentQueue = newArray; 383 | LM_RELEASE(newArray); 384 | } else { 385 | self.currentQueue = queue_; 386 | } 387 | 388 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidChangeShuffleMode:player:)]) { 389 | [self.delegate mediaPlayerDidChangeShuffleMode:enabled player:self]; 390 | } 391 | } 392 | 393 | - (void)setRepeatMode:(LMMediaRepeatMode)repeatMode 394 | { 395 | _repeatMode = repeatMode; 396 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidChangeRepeatMode:player:)]) { 397 | [self.delegate mediaPlayerDidChangeRepeatMode:repeatMode player:self]; 398 | } 399 | } 400 | 401 | #pragma mark - private 402 | 403 | - (void)setCurrentState:(LMMediaPlaybackState)state 404 | { 405 | if (state == playbackState_) { 406 | return; 407 | } 408 | 409 | if ([self.delegate respondsToSelector:@selector(mediaPlayerWillChangeState:)]) { 410 | [self.delegate mediaPlayerWillChangeState:state]; 411 | } 412 | 413 | if (state == LMMediaPlaybackStatePlaying) { 414 | [self updateLockScreenInfo]; 415 | NSError *e = nil; 416 | AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 417 | [audioSession setCategory:self.currentAudioSessionCategory error:&e]; 418 | [audioSession setActive:YES error:NULL]; 419 | } 420 | 421 | playbackState_ = state; 422 | } 423 | 424 | - (UIImage *)thumbnailAtTime:(CGFloat)time 425 | { 426 | AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:[[player_ currentItem] asset]]; 427 | imageGenerator.appliesPreferredTrackTransform = YES; 428 | NSError *error = NULL; 429 | CMTime ctime = CMTimeMake(time, 1); 430 | CGImageRef imageRef = [imageGenerator copyCGImageAtTime:ctime actualTime:NULL error:&error]; 431 | LM_RELEASE(imageGenerator); 432 | UIImage *resultImage = [[UIImage alloc] initWithCGImage:imageRef]; 433 | LM_AUTORELEASE(resultImage); 434 | CGImageRelease(imageRef); 435 | 436 | return resultImage; 437 | } 438 | 439 | - (UIImage *)representativeThumbnail 440 | { 441 | return [self thumbnailAtTime:self.currentPlaybackDuration / 2]; 442 | } 443 | 444 | - (NSError *)setAudioSessionCategory:(NSString *)category 445 | { 446 | NSError *e = nil; 447 | AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 448 | [audioSession setCategory:category error:&e]; 449 | _currentAudioSessionCategory = category; 450 | return e; 451 | } 452 | #pragma mark - Observer 453 | - (void)observeValueForKeyPath:(NSString *)aPath ofObject:(id)anObject change:(NSDictionary *)aChange context:(void *)aContext 454 | { 455 | 456 | if (aContext == AudioControllerBufferingObservationContext) { 457 | 458 | AVPlayerItem *playerItem = (AVPlayerItem *)anObject; 459 | NSArray *times = playerItem.loadedTimeRanges; 460 | 461 | NSValue *value = [times firstObject]; 462 | 463 | if (value) { 464 | CMTimeRange range; 465 | [value getValue:&range]; 466 | float start = CMTimeGetSeconds(range.start); 467 | float duration = CMTimeGetSeconds(range.duration); 468 | 469 | CGFloat videoAvailable = start + duration; 470 | CGFloat totalDuration = CMTimeGetSeconds(self.corePlayer.currentItem.asset.duration); 471 | CGFloat progress = videoAvailable / totalDuration; 472 | 473 | // UI must be update on the main thread 474 | dispatch_async(dispatch_get_main_queue(), ^{ 475 | if ([self.delegate respondsToSelector:@selector(mediaPlayerDidUpdateStreamingProgress:player:media:)]) 476 | [self.delegate mediaPlayerDidUpdateStreamingProgress:progress player:self media:self.nowPlayingItem]; 477 | }); 478 | } 479 | } 480 | } 481 | @end 482 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayerView/LMMediaPlayerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayerView.h 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/10. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaPlayer.h" 10 | #import "LMProgressBarView.h" 11 | #import 12 | #import 13 | 14 | extern NSString *const LMMediaPlayerViewPlayButtonImageKey; 15 | extern NSString *const LMMediaPlayerViewPlayButtonSelectedImageKey; 16 | extern NSString *const LMMediaPlayerViewStopButtonImageKey; 17 | extern NSString *const LMMediaPlayerViewStopButtonSelectedImageKey; 18 | extern NSString *const LMMediaPlayerViewFullscreenButtonImageKey; 19 | extern NSString *const LMMediaPlayerViewFullscreenButtonSelectedImageKey; 20 | extern NSString *const LMMediaPlayerViewUnfullscreenButtonImageKey; 21 | extern NSString *const LMMediaPlayerViewUnfullscreenButtonSelectedImageKey; 22 | extern NSString *const LMMediaPlayerViewShuffleButtonShuffledImageKey; 23 | extern NSString *const LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey; 24 | extern NSString *const LMMediaPlayerViewShuffleButtonUnshuffledImageKey; 25 | extern NSString *const LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey; 26 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatOneImageKey; 27 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatOneSelectedImageKey; 28 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatAllImageKey; 29 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatAllSelectedImageKey; 30 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatNoneImageKey; 31 | extern NSString *const LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey; 32 | extern NSString *const LMMediaPlayerViewActionButtonImageKey; 33 | 34 | @class LMMediaPlayerView; 35 | 36 | @protocol LMMediaPlayerViewDelegate 37 | 38 | @required 39 | - (BOOL)mediaPlayerViewWillStartPlaying:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 40 | 41 | @optional 42 | - (void)mediaPlayerViewWillChangeState:(LMMediaPlayerView *)playerView state:(LMMediaPlaybackState)state; 43 | - (void)mediaPlayerViewDidStartPlaying:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 44 | - (void)mediaPlayerViewDidFinishPlaying:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 45 | - (void)mediaPlayerViewDidChangeCurrentTime:(LMMediaPlayerView *)playerView; 46 | - (void)mediaPlayerViewDidChangeRepeatMode:(LMMediaRepeatMode)mode playerView:(LMMediaPlayerView *)playerView; 47 | - (void)mediaPlayerViewDidChangeShuffleMode:(BOOL)enabled playerView:(LMMediaPlayerView *)playerView; 48 | - (void)mediaPlayerViewWillChangeFullscreenMode:(BOOL)fullscreen; 49 | - (void)mediaPlayerViewDidChangeFullscreenMode:(BOOL)fullscreen; 50 | - (void)mediaPlayerViewDidUpdateStreamingProgress:(float)progress playerView:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 51 | - (void)mediaPlayerViewDidFailedWithError:(NSError *)error playerView:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 52 | - (void)mediaPlayerViewWillStartLoading:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 53 | - (void)mediaPlayerViewDidEndLoading:(LMMediaPlayerView *)playerView media:(LMMediaItem *)media; 54 | 55 | @end 56 | 57 | @interface LMMediaPlayerView : UIView 58 | 59 | @property (nonatomic, assign) id delegate; 60 | @property (nonatomic, readonly) LMMediaPlayer *mediaPlayer; 61 | @property (nonatomic, unsafe_unretained) IBOutlet UISlider *currentTimeSlider; 62 | @property (nonatomic, unsafe_unretained) IBOutlet LMProgressBarView *currentProgressView; 63 | @property (nonatomic, unsafe_unretained) IBOutlet UIActivityIndicatorView *activityIndicator; 64 | @property (nonatomic, strong) IBOutlet NSLayoutConstraint *activityIndicatorWidth; 65 | @property (nonatomic, unsafe_unretained) IBOutlet UILabel *titleLabel; 66 | @property (nonatomic, readonly) BOOL isFullscreen; 67 | @property (nonatomic, unsafe_unretained) IBOutlet UIButton *nextButton; 68 | @property (nonatomic, unsafe_unretained) IBOutlet UIButton *previousButton; 69 | @property (nonatomic, readonly) UIButton *actionButton; 70 | @property (nonatomic, assign) BOOL userInterfaceHidden; 71 | @property (nonatomic, readonly) BOOL bluredUserInterface; 72 | 73 | + (instancetype)sharedPlayerView; 74 | + (instancetype)create; 75 | - (void)setUserInterfaceHidden:(BOOL)userInterfaceHidden animated:(BOOL)animated; 76 | - (void)setHeaderViewHidden:(BOOL)hidden; 77 | - (void)setFooterViewHidden:(BOOL)hidden; 78 | - (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated; 79 | - (void)setFullscreen:(BOOL)fullscreen; 80 | - (void)setButtonImages:(NSDictionary *)info; 81 | - (void)setBluredUserInterface:(BOOL)bluredUserInterface visualEffect:(UIVisualEffect *)effect; 82 | - (void)setProgressBarBorderColor:(UIColor *)borderColor backgroundColor:(UIColor *)backgroundColor fillColor:(UIColor *)fillColor minTrackColor:(UIColor *)minTrackColor thumbTintColor:(UIColor *)thumbTintColor; 83 | - (void)setProgressBarThumbImage:(UIImage *)image; 84 | @end 85 | -------------------------------------------------------------------------------- /Pod/Classes/MediaPlayerView/LMMediaPlayerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMMediaPlayerView.m 3 | // iPodMusicSample 4 | // 5 | // Created by Akira Matsuda on 2014/01/10. 6 | // Copyright (c) 2014年 Akira Matsuda. All rights reserved. 7 | // 8 | 9 | #import "LMMediaPlayerView.h" 10 | #import "LMMediaPlayerHelper.h" 11 | #import 12 | #import 13 | 14 | static CGFloat const kFullscreenTransitionDuration = 0.2; 15 | static CGFloat const kActionButtonDefaultEdgeLength = 35; 16 | static CGFloat const kActionButtonDefaultRightMergin = 8; 17 | 18 | NSString *const LMMediaPlayerViewPlayButtonImageKey = @"playButtonImageKey"; 19 | NSString *const LMMediaPlayerViewPlayButtonSelectedImageKey = @"playButtonSelectedImageKey"; 20 | NSString *const LMMediaPlayerViewStopButtonImageKey = @"stopButtonImageKey"; 21 | NSString *const LMMediaPlayerViewStopButtonSelectedImageKey = @"stopButtonSelectedImageKey"; 22 | NSString *const LMMediaPlayerViewFullscreenButtonImageKey = @"fullscreenButtonImageKey"; 23 | NSString *const LMMediaPlayerViewFullscreenButtonSelectedImageKey = @"fullscreenButtonSelectedImageKey"; 24 | NSString *const LMMediaPlayerViewUnfullscreenButtonImageKey = @"unfullscreenButtonImageKey"; 25 | NSString *const LMMediaPlayerViewUnfullscreenButtonSelectedImageKey = @"unfullscreenButtonSelectedImageKey"; 26 | NSString *const LMMediaPlayerViewShuffleButtonShuffledImageKey = @"shuffleButtonShuffledImageKey"; 27 | NSString *const LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey = @"shuffleButtonShuffledSelectedImageKey"; 28 | NSString *const LMMediaPlayerViewShuffleButtonUnshuffledImageKey = @"shuffleButtonUnshuffledImageKey"; 29 | NSString *const LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey = @"shuffleButtonUnshuffledSelectedImageKey"; 30 | NSString *const LMMediaPlayerViewRepeatButtonRepeatOneImageKey = @"repeatButtonRepeatOneImageKey"; 31 | NSString *const LMMediaPlayerViewRepeatButtonRepeatOneSelectedImageKey = @"repeatButtonRepeatOneSelectedImageKey"; 32 | NSString *const LMMediaPlayerViewRepeatButtonRepeatAllImageKey = @"repeatButtonRepeatAllImageKey"; 33 | NSString *const LMMediaPlayerViewRepeatButtonRepeatAllSelectedImageKey = @"repeatButtonRepeatAllSelectedImageKey"; 34 | NSString *const LMMediaPlayerViewRepeatButtonRepeatNoneImageKey = @"repeatButtonRepeatNoneImageKey"; 35 | NSString *const LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey = @"repeatButtonRepeatNoneSelectedImageKey"; 36 | NSString *const LMMediaPlayerViewActionButtonImageKey = @"LMMediaPlayerViewActionButtonImageKey"; 37 | 38 | @interface UIViewController (LMMediaPlayerPrefersStatusBarHidden) 39 | 40 | - (void)mediaPlayerPrefersStatusBarHidden:(BOOL)hidden; 41 | 42 | @end 43 | 44 | @interface LMMediaPlayerFullscreenViewController : UIViewController 45 | 46 | @end 47 | 48 | @implementation LMMediaPlayerFullscreenViewController 49 | 50 | - (BOOL)shouldAutorotate 51 | { 52 | return YES; 53 | } 54 | 55 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations 56 | { 57 | return UIInterfaceOrientationMaskAllButUpsideDown; 58 | } 59 | 60 | - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation 61 | { 62 | return UIInterfaceOrientationPortrait; 63 | } 64 | 65 | @end 66 | 67 | @interface LMMediaPlayerView () { 68 | __unsafe_unretained IBOutlet UILabel *playbackTimeLabel_; 69 | __unsafe_unretained IBOutlet UILabel *remainingTimeLabel_; 70 | __unsafe_unretained IBOutlet UIView *headerView_; 71 | __unsafe_unretained IBOutlet UIView *footerView_; 72 | __unsafe_unretained IBOutlet UIImageView *artworkImageView_; 73 | __unsafe_unretained IBOutlet UIButton *playButton_; 74 | __unsafe_unretained IBOutlet UIButton *shuffleButton_; 75 | __unsafe_unretained IBOutlet UIButton *repeatButton_; 76 | __unsafe_unretained IBOutlet UIButton *fullscreenButton_; 77 | __unsafe_unretained IBOutlet UIButton *actionButton_; 78 | __unsafe_unretained IBOutlet NSLayoutConstraint *actionButtonWidth_; 79 | __unsafe_unretained IBOutlet NSLayoutConstraint *actionButtonRightMergin; 80 | BOOL fullscreen_; 81 | BOOL seeking_; 82 | BOOL needToSetPlayer_; 83 | UIView *superView_; 84 | NSMutableDictionary *buttonImages_; 85 | AVPlayerLayer *playerLayer_; 86 | 87 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 88 | UIVisualEffectView *headerBlurView_; 89 | UIVisualEffectView *footerBlurView_; 90 | #endif 91 | 92 | UIWindow *mainWindow_; 93 | } 94 | 95 | @end 96 | 97 | @implementation LMMediaPlayerView 98 | 99 | @synthesize isFullscreen = fullscreen_; 100 | 101 | static LMMediaPlayerView *sharedPlayerView; 102 | 103 | + (instancetype)sharedPlayerView 104 | { 105 | static dispatch_once_t onceToken; 106 | dispatch_once(&onceToken, ^{ 107 | sharedPlayerView = [[self class] create]; 108 | }); 109 | return sharedPlayerView; 110 | } 111 | 112 | + (instancetype)create 113 | { 114 | return [[UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle bundleForClass:[self class]]] instantiateWithOwner:nil options:nil][0]; 115 | } 116 | 117 | - (instancetype)initWithFrame:(CGRect)frame 118 | { 119 | self = [super initWithFrame:frame]; 120 | if (self) { 121 | [self setup]; 122 | } 123 | 124 | return self; 125 | } 126 | 127 | - (void)drawRect:(CGRect)rect 128 | { 129 | [super drawRect:rect]; 130 | 131 | if (needToSetPlayer_) { 132 | [playerLayer_ setPlayer:self.mediaPlayer.corePlayer]; 133 | needToSetPlayer_ = NO; 134 | } 135 | } 136 | 137 | - (void)dealloc 138 | { 139 | _mediaPlayer.delegate = nil; 140 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; 141 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; 142 | 143 | LM_RELEASE(playbackTimeLabel_); 144 | LM_RELEASE(remainingTimeLabel_); 145 | LM_RELEASE(headerView_); 146 | LM_RELEASE(footerView_); 147 | LM_RELEASE(artworkImageView_); 148 | LM_RELEASE(playButton_); 149 | LM_RELEASE(_nextButton); 150 | LM_RELEASE(_previousButton); 151 | LM_RELEASE(shuffleButton_); 152 | LM_RELEASE(repeatButton_); 153 | LM_RELEASE(fullscreenButton_); 154 | LM_RELEASE(_mediaPlayer); 155 | LM_RELEASE(buttonImages_); 156 | LM_DEALLOC(super); 157 | } 158 | 159 | - (void)awakeFromNib 160 | { 161 | [super awakeFromNib]; 162 | [self setup]; 163 | [self setupUserInterface]; 164 | } 165 | 166 | - (void)layoutSubviews 167 | { 168 | // resize your layers based on the view's new bounds 169 | [super layoutSubviews]; 170 | playerLayer_.frame = self.bounds; 171 | } 172 | 173 | - (UIButton *)actionButton 174 | { 175 | return actionButton_; 176 | } 177 | 178 | - (void)setBluredUserInterface:(BOOL)bluredUserInterface visualEffect:(UIVisualEffect *)effect 179 | { 180 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 181 | if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) { 182 | _bluredUserInterface = bluredUserInterface; 183 | if (_bluredUserInterface == YES) { 184 | if (headerBlurView_ == nil) { 185 | headerBlurView_ = [[UIVisualEffectView alloc] initWithEffect:effect]; 186 | footerBlurView_ = [[UIVisualEffectView alloc] initWithEffect:effect]; 187 | 188 | headerBlurView_.frame = headerView_.bounds; 189 | headerBlurView_.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 190 | 191 | footerBlurView_.frame = footerView_.bounds; 192 | footerBlurView_.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 193 | 194 | [headerView_ insertSubview:headerBlurView_ atIndex:0]; 195 | [footerView_ insertSubview:footerBlurView_ atIndex:0]; 196 | } 197 | 198 | headerView_.backgroundColor = [UIColor clearColor]; 199 | footerView_.backgroundColor = [UIColor clearColor]; 200 | } else { 201 | UIColor *backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.400]; 202 | headerView_.backgroundColor = backgroundColor; 203 | footerView_.backgroundColor = backgroundColor; 204 | } 205 | 206 | headerBlurView_.hidden = footerBlurView_.hidden = !_bluredUserInterface; 207 | } 208 | #endif 209 | } 210 | 211 | - (void)setProgressBarBorderColor:(UIColor *)borderColor backgroundColor:(UIColor *)backgroundColor fillColor:(UIColor *)fillColor minTrackColor:(UIColor *)minTrackColor thumbTintColor:(UIColor *)thumbTintColor 212 | { 213 | self.currentProgressView.barBorderColor = borderColor; 214 | self.currentProgressView.barBackgroundColor = backgroundColor; 215 | self.currentProgressView.barFillColor = fillColor; 216 | self.currentProgressView.barMinimumTrackFillColor = minTrackColor; 217 | 218 | self.currentTimeSlider.thumbTintColor = thumbTintColor; 219 | } 220 | 221 | - (void)setProgressBarThumbImage:(UIImage *)image 222 | { 223 | [self.currentTimeSlider setThumbImage:image forState:UIControlStateNormal]; 224 | } 225 | 226 | #pragma mark - 227 | 228 | - (void)setup 229 | { 230 | [self setTranslatesAutoresizingMaskIntoConstraints:YES]; 231 | self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 232 | 233 | mainWindow_ = [[UIApplication sharedApplication] keyWindow]; 234 | if (mainWindow_ == nil) { 235 | mainWindow_ = [[UIApplication sharedApplication] windows][0]; 236 | } 237 | LM_RETAIN(mainWindow_); 238 | 239 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerBecomeForgroundMode:) name:UIApplicationWillEnterForegroundNotification object:nil]; 240 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerBecomeBackgroundMode:) name:UIApplicationDidEnterBackgroundNotification object:nil]; 241 | 242 | needToSetPlayer_ = NO; 243 | _userInterfaceHidden = NO; 244 | 245 | _mediaPlayer = [[LMMediaPlayer alloc] init]; 246 | _mediaPlayer.delegate = self; 247 | } 248 | 249 | - (void)setupUserInterface 250 | { 251 | UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reverseUserInterfaceHidden)]; 252 | [self addGestureRecognizer:gesture]; 253 | LM_RELEASE(gesture); 254 | 255 | artworkImageView_.contentMode = UIViewContentModeScaleAspectFit; 256 | 257 | [_currentTimeSlider addTarget:self action:@selector(beginSeek:) forControlEvents:UIControlEventTouchDown]; 258 | [_currentTimeSlider addTarget:self action:@selector(seekPositionChanged:) forControlEvents:UIControlEventValueChanged]; 259 | [_currentTimeSlider addTarget:self action:@selector(endSeek:) forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchCancel)]; 260 | 261 | [playButton_ addTarget:self action:@selector(changePlaybackState:) forControlEvents:UIControlEventTouchUpInside]; 262 | [_nextButton addTarget:self action:@selector(fourcePlayNextMedia) forControlEvents:UIControlEventTouchUpInside]; 263 | [_previousButton addTarget:self action:@selector(fourcePlayPreviousMedia) forControlEvents:UIControlEventTouchUpInside]; 264 | 265 | UIColor *backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.400]; 266 | footerView_.backgroundColor = headerView_.backgroundColor = backgroundColor; 267 | [_mediaPlayer setShuffleEnabled:NO]; 268 | [_mediaPlayer setRepeatMode:LMMediaRepeatModeDefault]; 269 | 270 | buttonImages_ = [@{ LMMediaPlayerViewPlayButtonImageKey : [[self class] imageForFilename:@"play"], 271 | LMMediaPlayerViewPlayButtonSelectedImageKey : [[self class] imageForFilename:@"play"], 272 | LMMediaPlayerViewStopButtonImageKey : [[self class] imageForFilename:@"pause"], 273 | LMMediaPlayerViewStopButtonSelectedImageKey : [[self class] imageForFilename:@"pause"], 274 | LMMediaPlayerViewShuffleButtonShuffledImageKey : [[self class] imageForFilename:@"shuffle"], 275 | LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey : [[self class] imageForFilename:@"shuffle"], 276 | LMMediaPlayerViewShuffleButtonUnshuffledImageKey : [[self class] imageForFilename:@"unshuffle"], 277 | LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey : [[self class] imageForFilename:@"unshuffle"], 278 | LMMediaPlayerViewRepeatButtonRepeatNoneImageKey : [[self class] imageForFilename:@"repeat_none"], 279 | LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey : [[self class] imageForFilename:@"repeat_none"], 280 | LMMediaPlayerViewRepeatButtonRepeatOneImageKey : [[self class] imageForFilename:@"repeat_one"], 281 | LMMediaPlayerViewRepeatButtonRepeatOneSelectedImageKey : [[self class] imageForFilename:@"repeat_one"], 282 | LMMediaPlayerViewRepeatButtonRepeatAllImageKey : [[self class] imageForFilename:@"repeat_all"], 283 | LMMediaPlayerViewRepeatButtonRepeatAllSelectedImageKey : [[self class] imageForFilename:@"repeat_all"], 284 | LMMediaPlayerViewFullscreenButtonImageKey : [[self class] imageForFilename:@"fullscreen"], 285 | LMMediaPlayerViewFullscreenButtonSelectedImageKey : [[self class] imageForFilename:@"fullscreen"], 286 | LMMediaPlayerViewUnfullscreenButtonImageKey : [[self class] imageForFilename:@"unfullscreen"], 287 | LMMediaPlayerViewUnfullscreenButtonSelectedImageKey : [[self class] imageForFilename:@"unfullscreen"] } mutableCopy]; 288 | 289 | [playButton_.imageView setContentMode:UIViewContentModeScaleAspectFit]; 290 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonImageKey] forState:UIControlStateNormal]; 291 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonSelectedImageKey] forState:UIControlStateSelected]; 292 | 293 | [fullscreenButton_.imageView setContentMode:UIViewContentModeScaleAspectFit]; 294 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonImageKey] forState:UIControlStateNormal]; 295 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonSelectedImageKey] forState:UIControlStateSelected]; 296 | 297 | [repeatButton_.imageView setContentMode:UIViewContentModeScaleAspectFit]; 298 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneImageKey] forState:UIControlStateNormal]; 299 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey] forState:UIControlStateSelected]; 300 | 301 | [shuffleButton_.imageView setContentMode:UIViewContentModeScaleAspectFit]; 302 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledImageKey] forState:UIControlStateNormal]; 303 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey] forState:UIControlStateSelected]; 304 | 305 | [actionButton_.imageView setContentMode:UIViewContentModeScaleAspectFit]; 306 | actionButtonWidth_.constant = 0; 307 | actionButtonRightMergin.constant = 0; 308 | 309 | self.currentProgressView.barBorderWidth = 1.0f; 310 | self.currentProgressView.barInnerPadding = 1.0f; 311 | 312 | [self setProgressBarBorderColor:[UIColor whiteColor] 313 | backgroundColor:[UIColor clearColor] 314 | fillColor:[UIColor whiteColor] 315 | minTrackColor:[UIColor whiteColor] 316 | thumbTintColor:[UIColor whiteColor]]; 317 | 318 | [self.currentTimeSlider setMinimumTrackImage:[UIImage new] forState:UIControlStateNormal]; 319 | [self.currentTimeSlider setMaximumTrackImage:[UIImage new] forState:UIControlStateNormal]; 320 | 321 | [self setProgressBarBorderColor:[UIColor whiteColor] backgroundColor:[UIColor clearColor] fillColor:[UIColor whiteColor] minTrackColor:[UIColor colorWithRed:0.188 green:0.514 blue:0.984 alpha:1.000] thumbTintColor:[UIColor whiteColor]]; 322 | } 323 | 324 | - (void)mediaPlayerBecomeForgroundMode:(NSNotification *)notification 325 | { 326 | needToSetPlayer_ = YES; 327 | [self setNeedsDisplay]; 328 | } 329 | 330 | - (void)mediaPlayerBecomeBackgroundMode:(NSNotification *)notification 331 | { 332 | double delayInSeconds = 0.01; 333 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 334 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { 335 | [self->playerLayer_ setPlayer:nil]; 336 | if (self.mediaPlayer.playbackState == LMMediaPlaybackStatePlaying) { 337 | [self.mediaPlayer play]; 338 | } 339 | }); 340 | } 341 | 342 | #pragma mark LMMediaPlayerDelegate 343 | 344 | - (void)mediaPlayerWillChangeState:(LMMediaPlaybackState)state 345 | { 346 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewWillChangeState:state:)]) { 347 | [self.delegate mediaPlayerViewWillChangeState:self state:state]; 348 | } 349 | 350 | if (state == LMMediaPlaybackStateStopped || state == LMMediaPlaybackStatePaused) { 351 | if (state == LMMediaPlaybackStateStopped) { 352 | playerLayer_.hidden = YES; 353 | } 354 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonImageKey] ?: nil forState:UIControlStateNormal]; 355 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonSelectedImageKey] ?: nil forState:UIControlStateSelected]; 356 | } else { 357 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewStopButtonImageKey] ?: nil forState:UIControlStateNormal]; 358 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewStopButtonSelectedImageKey] ?: nil forState:UIControlStateSelected]; 359 | } 360 | } 361 | 362 | - (BOOL)mediaPlayerWillStartPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media 363 | { 364 | BOOL result = NO; 365 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewWillStartPlaying:media:)] == YES) { 366 | result = [self.delegate mediaPlayerViewWillStartPlaying:self media:media]; 367 | } else { 368 | result = YES; 369 | } 370 | self.titleLabel.text = media.title; 371 | 372 | return result; 373 | } 374 | 375 | - (void)mediaPlayerDidStartPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media 376 | { 377 | if (media.isVideo) { 378 | artworkImageView_.hidden = YES; 379 | self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 380 | if (playerLayer_ == nil) { 381 | playerLayer_ = [AVPlayerLayer playerLayerWithPlayer:player.corePlayer]; 382 | playerLayer_.frame = self.frame; 383 | [self.layer insertSublayer:playerLayer_ atIndex:0]; 384 | } 385 | needToSetPlayer_ = YES; 386 | 387 | playerLayer_.hidden = NO; 388 | } else { 389 | playerLayer_.hidden = YES; 390 | artworkImageView_.hidden = NO; 391 | artworkImageView_.image = [media artworkImageWithSize:self.frame.size]; 392 | } 393 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidStartPlaying:media:)]) { 394 | [self.delegate mediaPlayerViewDidStartPlaying:self media:media]; 395 | } 396 | } 397 | 398 | - (void)mediaPlayerDidFinishPlaying:(LMMediaPlayer *)player media:(LMMediaItem *)media 399 | { 400 | _currentTimeSlider.value = 1.0; 401 | _currentProgressView.currentProgress = _currentTimeSlider.value; 402 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidFinishPlaying:media:)]) { 403 | [self.delegate mediaPlayerViewDidFinishPlaying:self media:media]; 404 | } 405 | } 406 | 407 | - (void)mediaPlayerDidChangeCurrentTime:(LMMediaPlayer *)player 408 | { 409 | if (seeking_ == NO) { 410 | _currentTimeSlider.value = player.currentPlaybackTime / player.currentPlaybackDuration; 411 | _currentProgressView.currentProgress = _currentTimeSlider.value; 412 | 413 | NSMutableString *durationString = [NSMutableString new]; 414 | NSInteger duration = (NSInteger)player.currentPlaybackTime; 415 | if (duration / (60 * 60) > 0) { 416 | [durationString appendFormat:@"%02ld:", 417 | (long int)duration / (60 * 60)]; 418 | duration /= 60 * 60; 419 | } 420 | [durationString appendFormat:@"%02ld:", (long int)duration / 60]; 421 | duration %= 60; 422 | [durationString appendFormat:@"%02ld", (long int)duration]; 423 | playbackTimeLabel_.text = durationString; 424 | LM_RELEASE(durationString); 425 | 426 | durationString = [[NSMutableString alloc] initWithString:@"-"]; 427 | duration = (NSInteger)fabs(player.currentPlaybackTime - player.currentPlaybackDuration); 428 | if (duration / (60 * 60) > 0) { 429 | [durationString appendFormat:@"%02ld:", 430 | (long int)duration / (60 * 60)]; 431 | duration /= 60 * 60; 432 | } 433 | [durationString appendFormat:@"%02ld:", (long int)duration / 60]; 434 | duration %= 60; 435 | [durationString appendFormat:@"%02ld", (long int)duration]; 436 | remainingTimeLabel_.text = durationString; 437 | LM_RELEASE(durationString); 438 | } 439 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidChangeCurrentTime:)]) { 440 | [self.delegate mediaPlayerViewDidChangeCurrentTime:self]; 441 | } 442 | } 443 | 444 | - (void)mediaPlayerDidChangeRepeatMode:(LMMediaRepeatMode)mode player:(LMMediaPlayer *)player 445 | { 446 | [self setRepeatButtonImageWithRepeatMode:mode]; 447 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidChangeRepeatMode:playerView:)]) { 448 | [self.delegate mediaPlayerViewDidChangeRepeatMode:mode playerView:self]; 449 | } 450 | } 451 | 452 | - (void)mediaPlayerDidChangeShuffleMode:(BOOL)enabled player:(LMMediaPlayer *)player 453 | { 454 | [self setShuffleButtonImageWithShuffleMode:enabled]; 455 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidChangeShuffleMode:playerView:)]) { 456 | [self.delegate mediaPlayerViewDidChangeShuffleMode:enabled playerView:self]; 457 | } 458 | } 459 | 460 | - (void)mediaPlayerDidUpdateStreamingProgress:(float)progress player:(LMMediaPlayer *)player media:(LMMediaItem *)media 461 | { 462 | [_currentProgressView setProgress:progress]; 463 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidUpdateStreamingProgress:playerView:media:)]) { 464 | [self.delegate mediaPlayerViewDidUpdateStreamingProgress:progress playerView:self media:media]; 465 | } 466 | } 467 | 468 | - (void)mediaPlayerDidFailedWithError:(NSError *)error player:(LMMediaPlayer *)player media:(LMMediaItem *)media 469 | { 470 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidFailedWithError:playerView:media:)]) { 471 | [self.delegate mediaPlayerViewDidFailedWithError:error playerView:self media:media]; 472 | } 473 | } 474 | 475 | - (void)mediaPlayerWillStartLoading:(LMMediaPlayer *)player media:(LMMediaItem *)media 476 | { 477 | [self.activityIndicatorWidth setConstant:20.0]; 478 | [self.activityIndicator startAnimating]; 479 | 480 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewWillStartLoading:media:)]) { 481 | [self.delegate mediaPlayerViewWillStartLoading:self media:media]; 482 | } 483 | } 484 | 485 | - (void)mediaPlayerDidEndLoading:(LMMediaPlayer *)player media:(LMMediaItem *)media 486 | { 487 | [self.activityIndicatorWidth setConstant:0.0]; 488 | [self.activityIndicator stopAnimating]; 489 | 490 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidEndLoading:media:)]) { 491 | [self.delegate mediaPlayerViewDidEndLoading:self media:media]; 492 | } 493 | } 494 | #pragma mark - 495 | 496 | - (void)beginSeek:(id)sender 497 | { 498 | seeking_ = YES; 499 | } 500 | 501 | - (void)seekPositionChanged:(id)sender 502 | { 503 | NSMutableString *durationString = [NSMutableString new]; 504 | NSInteger currentTime = (NSInteger)_mediaPlayer.currentPlaybackDuration * _currentTimeSlider.value; 505 | NSInteger duration = currentTime; 506 | if (duration / (60 * 60) > 0) { 507 | [durationString appendFormat:@"%02ld:", 508 | (long int)duration / (60 * 60)]; 509 | duration /= 60 * 60; 510 | } 511 | [durationString appendFormat:@"%02ld:", (long int)duration / 60]; 512 | duration %= 60; 513 | [durationString appendFormat:@"%02ld", (long int)duration]; 514 | playbackTimeLabel_.text = durationString; 515 | LM_RELEASE(durationString); 516 | 517 | durationString = [[NSMutableString alloc] initWithString:@"-"]; 518 | duration = (NSInteger)_mediaPlayer.currentPlaybackDuration - currentTime; 519 | if (duration / (60 * 60) > 0) { 520 | [durationString appendFormat:@"%02ld:", 521 | (long int)duration / (60 * 60)]; 522 | duration /= 60 * 60; 523 | } 524 | [durationString appendFormat:@"%02ld:", (long int)duration / 60]; 525 | duration %= 60; 526 | [durationString appendFormat:@"%02ld", (long int)duration]; 527 | remainingTimeLabel_.text = durationString; 528 | LM_RELEASE(durationString); 529 | 530 | _currentProgressView.currentProgress = _currentTimeSlider.value; 531 | } 532 | 533 | - (void)endSeek:(id)sender 534 | { 535 | UISlider *slider = (UISlider *)sender; 536 | [_mediaPlayer seekTo:_mediaPlayer.currentPlaybackDuration * slider.value]; 537 | _currentProgressView.currentProgress = _currentTimeSlider.value; 538 | seeking_ = NO; 539 | } 540 | 541 | - (void)changePlaybackState:(id)sender 542 | { 543 | if ([_mediaPlayer playbackState] == LMMediaPlaybackStatePlaying) { 544 | [_mediaPlayer pause]; 545 | } else if ([_mediaPlayer playbackState] == LMMediaPlaybackStatePaused || [_mediaPlayer playbackState] == LMMediaPlaybackStateStopped) { 546 | [_mediaPlayer play]; 547 | } 548 | } 549 | 550 | - (void)reverseUserInterfaceHidden 551 | { 552 | if (seeking_ == NO) { 553 | [self setUserInterfaceHidden:!_userInterfaceHidden]; 554 | } 555 | } 556 | 557 | - (void)fourcePlayNextMedia 558 | { 559 | LMMediaRepeatMode repeatMode = _mediaPlayer.repeatMode; 560 | if (repeatMode == LMMediaRepeatModeOne) { 561 | _mediaPlayer.repeatMode = LMMediaRepeatModeDefault; 562 | } 563 | [_mediaPlayer playNextMedia]; 564 | _mediaPlayer.repeatMode = repeatMode; 565 | } 566 | 567 | - (void)fourcePlayPreviousMedia 568 | { 569 | LMMediaRepeatMode repeatMode = _mediaPlayer.repeatMode; 570 | if (repeatMode == LMMediaRepeatModeOne) { 571 | _mediaPlayer.repeatMode = LMMediaRepeatModeDefault; 572 | } 573 | [_mediaPlayer playPreviousMedia]; 574 | _mediaPlayer.repeatMode = repeatMode; 575 | } 576 | 577 | - (IBAction)shuffleButtonPressed:(id)sender 578 | { 579 | [_mediaPlayer setShuffleEnabled:!_mediaPlayer.shuffleMode]; 580 | 581 | if (_mediaPlayer.shuffleMode) { 582 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonShuffledImageKey] forState:UIControlStateNormal]; 583 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey] forState:UIControlStateSelected]; 584 | } else { 585 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledImageKey] forState:UIControlStateNormal]; 586 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey] forState:UIControlStateSelected]; 587 | } 588 | [self setRepeatButtonImageWithRepeatMode:_mediaPlayer.repeatMode]; 589 | } 590 | 591 | - (IBAction)repeatButtonPressed:(id)sender 592 | { 593 | switch (_mediaPlayer.repeatMode) { 594 | case LMMediaRepeatModeAll: { 595 | _mediaPlayer.repeatMode = LMMediaRepeatModeOne; 596 | } break; 597 | case LMMediaRepeatModeOne: { 598 | _mediaPlayer.repeatMode = LMMediaRepeatModeDefault; 599 | } break; 600 | case LMMediaRepeatModeDefault: { 601 | _mediaPlayer.repeatMode = LMMediaRepeatModeAll; 602 | } break; 603 | default: 604 | break; 605 | } 606 | [self setRepeatButtonImageWithRepeatMode:_mediaPlayer.repeatMode]; 607 | } 608 | 609 | - (void)setShuffleButtonImageWithShuffleMode:(BOOL)mode 610 | { 611 | if (mode) { 612 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonShuffledImageKey] forState:UIControlStateNormal]; 613 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey] forState:UIControlStateSelected]; 614 | } else { 615 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledImageKey] forState:UIControlStateNormal]; 616 | [shuffleButton_ setImage:buttonImages_[LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey] forState:UIControlStateSelected]; 617 | } 618 | } 619 | 620 | - (void)setRepeatButtonImageWithRepeatMode:(LMMediaRepeatMode)mode 621 | { 622 | switch (_mediaPlayer.repeatMode) { 623 | case LMMediaRepeatModeAll: { 624 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatAllImageKey] forState:UIControlStateNormal]; 625 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatAllSelectedImageKey] forState:UIControlStateSelected]; 626 | } break; 627 | case LMMediaRepeatModeOne: { 628 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatOneImageKey] forState:UIControlStateNormal]; 629 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatOneSelectedImageKey] forState:UIControlStateSelected]; 630 | } break; 631 | case LMMediaRepeatModeDefault: { 632 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneImageKey] forState:UIControlStateNormal]; 633 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey] forState:UIControlStateSelected]; 634 | } break; 635 | default: { 636 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneImageKey] forState:UIControlStateNormal]; 637 | [repeatButton_ setImage:buttonImages_[LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey] forState:UIControlStateSelected]; 638 | } break; 639 | } 640 | } 641 | 642 | - (IBAction)fullscreenButtonPressed:(id)sender 643 | { 644 | [self setFullscreen:!fullscreen_]; 645 | } 646 | 647 | #pragma mark - 648 | 649 | - (void)setHeaderViewHidden:(BOOL)hidden 650 | { 651 | headerView_.hidden = hidden; 652 | } 653 | 654 | - (void)setFooterViewHidden:(BOOL)hidden 655 | { 656 | footerView_.hidden = hidden; 657 | } 658 | 659 | - (void)setUserInterfaceHidden:(BOOL)userInterfaceHidden 660 | { 661 | [self setUserInterfaceHidden:userInterfaceHidden animated:NO]; 662 | } 663 | 664 | - (void)setUserInterfaceHidden:(BOOL)userInterfaceHidden animated:(BOOL)animated 665 | { 666 | NSTimeInterval duration = 0.3 * (animated ? 1 : 0); 667 | _userInterfaceHidden = userInterfaceHidden; 668 | if (userInterfaceHidden) { 669 | [UIView animateWithDuration:duration 670 | animations:^{ 671 | self->headerView_.alpha = 0; 672 | self->footerView_.alpha = 0; 673 | self->_currentTimeSlider.alpha = 0; 674 | self->_nextButton.superview.alpha = 0; 675 | self->_previousButton.superview.alpha = 0; 676 | } 677 | completion:^(BOOL finished) { 678 | }]; 679 | } else { 680 | [UIView animateWithDuration:duration 681 | animations:^{ 682 | self->headerView_.alpha = 1; 683 | self->footerView_.alpha = 1; 684 | self->_currentTimeSlider.alpha = 1; 685 | self->_nextButton.superview.alpha = 1; 686 | self->_previousButton.superview.alpha = 1; 687 | } 688 | completion:^(BOOL finished) { 689 | }]; 690 | } 691 | } 692 | 693 | - (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated 694 | { 695 | if (fullscreen_ == fullscreen) { 696 | return; 697 | } 698 | 699 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewWillChangeFullscreenMode:)]) { 700 | [self.delegate mediaPlayerViewWillChangeFullscreenMode:fullscreen]; 701 | } 702 | 703 | LMMediaPlayerFullscreenViewController *viewController = [[LMMediaPlayerFullscreenViewController alloc] init]; 704 | LM_AUTORELEASE(viewController); 705 | viewController.view.frame = [UIScreen mainScreen].bounds; 706 | 707 | if (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_7_0) { 708 | viewController.extendedLayoutIncludesOpaqueBars = YES; 709 | } 710 | 711 | CGRect newRect; 712 | if (fullscreen == NO) { 713 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonImageKey] forState:UIControlStateNormal]; 714 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonSelectedImageKey] forState:UIControlStateSelected]; 715 | 716 | newRect = superView_.bounds; 717 | self.frame = newRect; 718 | [superView_ addSubview:self]; 719 | LM_RELEASE(superView_); 720 | [mainWindow_ makeKeyAndVisible]; 721 | [[[UIApplication sharedApplication] delegate] setWindow:mainWindow_]; 722 | } else { 723 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewUnfullscreenButtonImageKey] forState:UIControlStateNormal]; 724 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewUnfullscreenButtonSelectedImageKey] forState:UIControlStateSelected]; 725 | superView_ = self.superview; 726 | LM_RETAIN(superView_); 727 | newRect = mainWindow_.frame; 728 | 729 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 730 | if (orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft) { 731 | if (floor(NSFoundationVersionNumber) >= NSFoundationVersionNumber_iOS_7_0) { 732 | newRect = CGRectMake(0, 0, CGRectGetWidth(mainWindow_.frame), CGRectGetHeight(mainWindow_.frame)); 733 | } 734 | } 735 | 736 | [self removeFromSuperview]; 737 | [viewController.view addSubview:self]; 738 | UIWindow *newWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 739 | newWindow.windowLevel = UIWindowLevelAlert; 740 | newWindow.rootViewController = viewController; 741 | [newWindow addSubview:viewController.view]; 742 | [newWindow makeKeyAndVisible]; 743 | [[[UIApplication sharedApplication] delegate] setWindow:newWindow]; 744 | LM_RELEASE(newWindow); 745 | } 746 | 747 | self.frame = newRect; 748 | if (animated) { 749 | self.alpha = 0; 750 | [UIView animateWithDuration:kFullscreenTransitionDuration 751 | animations:^{ 752 | self.alpha = 1; 753 | }]; 754 | } 755 | fullscreen_ = fullscreen; 756 | if ([self.delegate respondsToSelector:@selector(mediaPlayerViewDidChangeFullscreenMode:)]) { 757 | [self.delegate mediaPlayerViewDidChangeFullscreenMode:fullscreen]; 758 | } 759 | [[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]]; 760 | } 761 | 762 | - (void)setFullscreen:(BOOL)fullscreen 763 | { 764 | [self setFullscreen:fullscreen animated:YES]; 765 | } 766 | 767 | - (void)setButtonImages:(NSDictionary *)info 768 | { 769 | for (NSString *key in info) { 770 | buttonImages_[key] = info[key]; 771 | } 772 | if ([buttonImages_[LMMediaPlayerViewActionButtonImageKey] isKindOfClass:[UIImage class]]) { 773 | actionButtonWidth_.constant = kActionButtonDefaultEdgeLength; 774 | actionButtonRightMergin.constant = kActionButtonDefaultRightMergin; 775 | [actionButton_ setImage:info[LMMediaPlayerViewActionButtonImageKey] forState:UIControlStateNormal]; 776 | } else { 777 | actionButtonWidth_.constant = 0; 778 | actionButtonRightMergin.constant = 0; 779 | } 780 | [self updateButtonImages]; 781 | } 782 | 783 | - (void)updateButtonImages 784 | { 785 | if (self.mediaPlayer.playbackState == LMMediaPlaybackStatePlaying) { 786 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewStopButtonImageKey] forState:UIControlStateNormal]; 787 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewStopButtonSelectedImageKey] forState:UIControlStateSelected]; 788 | } else { 789 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonImageKey] forState:UIControlStateNormal]; 790 | [playButton_ setImage:buttonImages_[LMMediaPlayerViewPlayButtonSelectedImageKey] forState:UIControlStateSelected]; 791 | } 792 | 793 | if (self.isFullscreen) { 794 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewUnfullscreenButtonImageKey] forState:UIControlStateNormal]; 795 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewUnfullscreenButtonSelectedImageKey] forState:UIControlStateSelected]; 796 | } else { 797 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonImageKey] forState:UIControlStateNormal]; 798 | [fullscreenButton_ setImage:buttonImages_[LMMediaPlayerViewFullscreenButtonSelectedImageKey] forState:UIControlStateSelected]; 799 | } 800 | 801 | [self setRepeatButtonImageWithRepeatMode:self.mediaPlayer.repeatMode]; 802 | [self setShuffleButtonImageWithShuffleMode:self.mediaPlayer.shuffleMode]; 803 | } 804 | 805 | + (UIImage *)imageForFilename:(NSString *)filename 806 | { 807 | NSString *version = @"7"; 808 | if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { 809 | version = @"6"; 810 | } 811 | UIImage *image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@/%@.png", [[NSBundle bundleForClass:[self class]] pathForResource:@"LMMediaPlayerView" ofType:@"bundle"], version, filename]]; 812 | 813 | return image; 814 | } 815 | 816 | @end 817 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LMMediaPlayer 2 | 3 | [![CI Status](http://img.shields.io/travis/Akira Matsuda/LMMediaPlayer.svg?style=flat)](https://travis-ci.org/Akira Matsuda/LMMediaPlayer) 4 | [![Version](https://img.shields.io/cocoapods/v/LMMediaPlayer.svg?style=flat)](http://cocoadocs.org/docsets/LMMediaPlayer) 5 | [![License](https://img.shields.io/cocoapods/l/LMMediaPlayer.svg?style=flat)](http://cocoadocs.org/docsets/LMMediaPlayer) 6 | [![Platform](https://img.shields.io/cocoapods/p/LMMediaPlayer.svg?style=flat)](http://cocoadocs.org/docsets/LMMediaPlayer) 7 | 8 | LMMediaPlayer is a video and an audio player for iPhone with replaceable user interface. 9 | 10 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/1.png) 11 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/2.png) 12 | ![](https://raw.github.com/0x0c/LMMediaPlayer/master/images/3.png) 13 | 14 | ## Requirements 15 | 16 | - Runs on iOS 6.0 or later. 17 | 18 | ## Contribution 19 | 20 | Please use clang-format and use .clang-format file which is containing this repo. 21 | 22 | ## Installation 23 | 24 | LMMediaPlayer is available through [CocoaPods](http://cocoapods.org). To install 25 | it, simply add the following line to your Podfile: 26 | 27 | pod "LMMediaPlayer" 28 | 29 | ## Usage 30 | 31 | You can play **MPMediaItem** and **http streaming contents** with fullscreen or non-fullscreen mode. 32 | 33 | //Get shared player 34 | LMMediaPlayerView *player = [LMMediaPlayerView sharedPlayerView]; 35 | 36 | //Create media item with URL. 37 | LMMediaItem *item1 = [[LMMediaItem alloc] initWithInfo:@{ 38 | LMMediaItemInfoURLKey:[NSURL URLWithString:@"video or audio url"], 39 | LMMediaItemInfoContentTypeKey:@(LMMediaItemContentTypeVideo) 40 | }]; 41 | 42 | //Create media item with MPMediaItem. 43 | MPMediaItem *mediaItem = 44 | LMMediaItem *item2 = [[LMMediaItem alloc] initWithMetaMedia:mediaItem contentType:LMMediaItemContentTypeVideo]; 45 | 46 | //Add queue. 47 | [player.mediaPlayer addMedia:item1]; 48 | [player.mediaPlayer addMedia:item2]; 49 | 50 | //Play it! 51 | [player.mediaPlayer play]; 52 | 53 | without player view. 54 | 55 | //Get shared player 56 | LMMediaPlayer *player = [LMMediaPlayer sharedPlayer]; 57 | 58 | //Create media item with URL. 59 | LMMediaItem *item1 = [[LMMediaItem alloc] initWithInfo:@{ 60 | LMMediaItemInfoURLKey:[NSURL URLWithString:@"video or audio url"], 61 | LMMediaItemInfoContentTypeKey:@(LMMediaItemContentTypeVideo) 62 | }]; 63 | 64 | //Create media item with MPMediaItem. 65 | MPMediaItem *mediaItem = 66 | LMMediaItem *item2 = [[LMMediaItem alloc] initWithMetaMedia:mediaItem contentType:LMMediaItemContentTypeVideo]; 67 | 68 | //Add queue. 69 | [player addMedia:item1]; 70 | [player addMedia:item2]; 71 | 72 | //Play it! 73 | [player play]; 74 | 75 | Already implemented repeat mode and shuffle mode. 76 | 77 | LMMediaPlayerView *player = [LMMediaPlayerView sharedPlayerView]; 78 | player.mediaPlayer.repeatMode = LMMediaRepeatModeNone; 79 | player.mediaPlayer.repeatMode = LMMediaRepeatModeAll; 80 | player.mediaPlayer.repeatMode = LMMediaRepeatModeOne; 81 | 82 | [player.mediaPlayer setShuffleEnabled:YES]; 83 | 84 | To change user interface, use ```setButtonImages:``` and set images with these keys. 85 | 86 | | Keys | 87 | |:-----------| 88 | | LMMediaPlayerViewPlayButtonImageKey | 89 | | LMMediaPlayerViewPlayButtonSelectedImageKey | 90 | | LMMediaPlayerViewStopButtonImageKey | 91 | | LMMediaPlayerViewStopButtonSelectedImageKey | 92 | | LMMediaPlayerViewFullscreenButtonImageKey | 93 | | LMMediaPlayerViewFullscreenButtonSelectedImageKey | 94 | | LMMediaPlayerViewUnfullscreenButtonImageKey | 95 | | LMMediaPlayerViewUnfullscreenButtonSelectedImageKey | 96 | | LMMediaPlayerViewShuffleButtonShuffledImageKey | 97 | | LMMediaPlayerViewShuffleButtonShuffledSelectedImageKey | 98 | | LMMediaPlayerViewShuffleButtonUnshuffledImageKey | 99 | | LMMediaPlayerViewShuffleButtonUnshuffledSelectedImageKey | 100 | | LMMediaPlayerViewRepeatButtonRepeatOneImageKey | 101 | | LMMediaPlayerViewRepeatButtonRepeatOneSelectedImageKey | 102 | | LMMediaPlayerViewRepeatButtonRepeatAllImageKey | 103 | | LMMediaPlayerViewRepeatButtonRepeatAllSelectedImageKey | 104 | | LMMediaPlayerViewRepeatButtonRepeatNoneImageKey | 105 | | LMMediaPlayerViewRepeatButtonRepeatNoneSelectedImageKey | 106 | | LMMediaPlayerViewActionButtonImageKey | 107 | 108 | To show action button, set image with ```LMMediaPlayerViewActionButtonImageKey```. 109 | 110 | Use the following helper method for customising the progress bar: 111 | ```objective-c 112 | [[LMMediaPlayerView sharedPlayerView] setProgressBarBorderColor:[UIColor whiteColor] 113 | backgroundColor:[UIColor clearColor] 114 | fillColor:[UIColor lightGrayColor] 115 | minTrackColor:[UIColor redColor] 116 | thumbTintColor:[UIColor whiteColor]]; 117 | ``` 118 | Use the following helper method for customising the progress bar thumb: 119 | ```objective-c 120 | [[LMMediaPlayerView sharedPlayerView] setProgressBarThumbImage:[UIImage imageNamed:@"thumb"]]; 121 | ``` 122 | 123 | 124 | Of course, you can play video or audio in background mode and can control with control center. 125 | If you want to play with fullscreen mode, please add "View controller-based status bar appearance" key and set value with "NO" at your Info.plist 126 | 127 | ## Author 128 | 129 | Akira Matsuda, [akira.matsuda@me.com](mailto:akira.matsuda@me.com) 130 | 131 | ## License 132 | 133 | LMMediaPlayer is available under the MIT license. See the LICENSE file for more info. 134 | -------------------------------------------------------------------------------- /images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/images/1.png -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/images/3.png -------------------------------------------------------------------------------- /sample.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x0c/LMMediaPlayer/5ad684f795bcca92d323a050e2609384bc6a803b/sample.mp4 --------------------------------------------------------------------------------