diff --git a/.gitignore b/.gitignore index 6142941..03b4b51 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ fastlane/screenshots # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ + +libs/build/ + +Example-Carthage/Carthage/ diff --git a/Carthage.xcconfig b/Carthage.xcconfig new file mode 100644 index 0000000..22945b3 --- /dev/null +++ b/Carthage.xcconfig @@ -0,0 +1,3 @@ +ONLY_ACTIVE_ARCH = YES +ENABLE_BITCODE = YES +OTHER_CFLAGS = "-fembed-bitcode" diff --git a/DLGPlayer.xcodeproj/project.pbxproj b/DLGPlayer.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cc77c21 --- /dev/null +++ b/DLGPlayer.xcodeproj/project.pbxproj @@ -0,0 +1,848 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 23241CAE22A0B45400871C58 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23241CAD22A0B45400871C58 /* SecondViewController.swift */; }; + 235409B421BA05A100533B9B /* DLGPlayerDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC291821B810B40012243A /* DLGPlayerDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409B521BA05A400533B9B /* DLGPlayerAudioManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC291921B810B40012243A /* DLGPlayerAudioManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409B621BA05A900533B9B /* DLGPlayerDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC293621B810B50012243A /* DLGPlayerDef.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409B721BA05A900533B9B /* DLGPlayerUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC293721B810B50012243A /* DLGPlayerUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409B821BA05B500533B9B /* DLGPlayerVideoYUVFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC292821B810B50012243A /* DLGPlayerVideoYUVFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409B921BA05B500533B9B /* DLGPlayerVideoFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC292921B810B50012243A /* DLGPlayerVideoFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409BA21BA05B500533B9B /* DLGPlayerFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC292C21B810B50012243A /* DLGPlayerFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409BB21BA05B500533B9B /* DLGPlayerVideoRGBFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC292D21B810B50012243A /* DLGPlayerVideoRGBFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409BC21BA05B500533B9B /* DLGPlayerAudioFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC293021B810B50012243A /* DLGPlayerAudioFrame.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409BD21BA05BC00533B9B /* DLGPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC293421B810B50012243A /* DLGPlayerView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409C621BA177A00533B9B /* DLGSimplePlayerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 235409C421BA177A00533B9B /* DLGSimplePlayerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 235409C721BA177A00533B9B /* DLGSimplePlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 235409C521BA177A00533B9B /* DLGSimplePlayerViewController.m */; }; + 237758B421CA5658000B6B7F /* DLGPlayerControlStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 237758B221CA5658000B6B7F /* DLGPlayerControlStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 237758B521CA5658000B6B7F /* DLGPlayerControlStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 237758B321CA5658000B6B7F /* DLGPlayerControlStatus.m */; }; + 239BC97121C234CF00DEEC73 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 239BC97021C234CF00DEEC73 /* AppDelegate.swift */; }; + 239BC97321C234CF00DEEC73 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 239BC97221C234CF00DEEC73 /* RootViewController.swift */; }; + 239BC97621C234CF00DEEC73 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 239BC97421C234CF00DEEC73 /* Main.storyboard */; }; + 239BC97821C234D000DEEC73 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 239BC97721C234D000DEEC73 /* Assets.xcassets */; }; + 239BC97B21C234D000DEEC73 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 239BC97921C234D000DEEC73 /* LaunchScreen.storyboard */; }; + 239BC99121C2362100DEEC73 /* DLGPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23DC26CE21B80F5A0012243A /* DLGPlayer.framework */; }; + 239BC99321C2457600DEEC73 /* DLGPlayer.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 23DC26CE21B80F5A0012243A /* DLGPlayer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 239F11B12398F48000CF88D6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 239F11B02398F48000CF88D6 /* ViewController.swift */; }; + 23DC293921B810B50012243A /* DLGPlayerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC291421B810B40012243A /* DLGPlayerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 23DC293A21B810B50012243A /* DLGPlayerDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC291621B810B40012243A /* DLGPlayerDecoder.m */; }; + 23DC293B21B810B50012243A /* DLGPlayerAudioManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC291721B810B40012243A /* DLGPlayerAudioManager.m */; }; + 23DC293E21B810B50012243A /* DLGPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 23DC291A21B810B50012243A /* DLGPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 23DC293F21B810B50012243A /* DLGPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC291B21B810B50012243A /* DLGPlayerViewController.m */; }; + 23DC294021B810B50012243A /* DLGPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC291C21B810B50012243A /* DLGPlayer.m */; }; + 23DC294121B810B50012243A /* DLGPlayerStrings.strings in Resources */ = {isa = PBXBuildFile; fileRef = 23DC291E21B810B50012243A /* DLGPlayerStrings.strings */; }; + 23DC294221B810B50012243A /* DLGPlayerRotationScaleVertexShader.glsl in Resources */ = {isa = PBXBuildFile; fileRef = 23DC292121B810B50012243A /* DLGPlayerRotationScaleVertexShader.glsl */; }; + 23DC294321B810B50012243A /* DLGPlayerYUVFragmentShader.glsl in Resources */ = {isa = PBXBuildFile; fileRef = 23DC292221B810B50012243A /* DLGPlayerYUVFragmentShader.glsl */; }; + 23DC294421B810B50012243A /* DLGPlayerRGBFragmentShader.glsl in Resources */ = {isa = PBXBuildFile; fileRef = 23DC292321B810B50012243A /* DLGPlayerRGBFragmentShader.glsl */; }; + 23DC294521B810B50012243A /* DLGPlayerRotationVertexShader.glsl in Resources */ = {isa = PBXBuildFile; fileRef = 23DC292521B810B50012243A /* DLGPlayerRotationVertexShader.glsl */; }; + 23DC294621B810B50012243A /* DLGPlayerVertexShader.glsl in Resources */ = {isa = PBXBuildFile; fileRef = 23DC292621B810B50012243A /* DLGPlayerVertexShader.glsl */; }; + 23DC294921B810B50012243A /* DLGPlayerVideoRGBFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC292A21B810B50012243A /* DLGPlayerVideoRGBFrame.m */; }; + 23DC294A21B810B50012243A /* DLGPlayerAudioFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC292B21B810B50012243A /* DLGPlayerAudioFrame.m */; }; + 23DC294D21B810B50012243A /* DLGPlayerVideoFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC292E21B810B50012243A /* DLGPlayerVideoFrame.m */; }; + 23DC294E21B810B50012243A /* DLGPlayerVideoYUVFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC292F21B810B50012243A /* DLGPlayerVideoYUVFrame.m */; }; + 23DC295021B810B50012243A /* DLGPlayerFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC293121B810B50012243A /* DLGPlayerFrame.m */; }; + 23DC295121B810B50012243A /* DLGPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC293321B810B50012243A /* DLGPlayerView.m */; }; + 23DC295521B810B50012243A /* DLGPlayerUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 23DC293821B810B50012243A /* DLGPlayerUtils.m */; }; + 23E013A6230E7B5D00B9BB0E /* Shader.metal in Sources */ = {isa = PBXBuildFile; fileRef = 23E013A5230E7B5B00B9BB0E /* Shader.metal */; }; + 23E013AA230E7B6B00B9BB0E /* DLGPlayerVideoFrameView.h in Headers */ = {isa = PBXBuildFile; fileRef = 23E013A7230E7B6B00B9BB0E /* DLGPlayerVideoFrameView.h */; }; + 23E013AB230E7B6B00B9BB0E /* MetalPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 23E013A8230E7B6B00B9BB0E /* MetalPlayerView.h */; }; + 23E013AC230E7B6B00B9BB0E /* MetalPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 23E013A9230E7B6B00B9BB0E /* MetalPlayerView.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 239BC99221C2456B00DEEC73 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 239BC99321C2457600DEEC73 /* DLGPlayer.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 23241CAD22A0B45400871C58 /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; }; + 235409C421BA177A00533B9B /* DLGSimplePlayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DLGSimplePlayerViewController.h; sourceTree = ""; }; + 235409C521BA177A00533B9B /* DLGSimplePlayerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DLGSimplePlayerViewController.m; sourceTree = ""; }; + 237758B221CA5658000B6B7F /* DLGPlayerControlStatus.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DLGPlayerControlStatus.h; sourceTree = ""; }; + 237758B321CA5658000B6B7F /* DLGPlayerControlStatus.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerControlStatus.m; sourceTree = ""; }; + 239BC96E21C234CF00DEEC73 /* DLGPlayerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DLGPlayerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 239BC97021C234CF00DEEC73 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 239BC97221C234CF00DEEC73 /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; + 239BC97521C234CF00DEEC73 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 239BC97721C234D000DEEC73 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 239BC97A21C234D000DEEC73 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 239BC97C21C234D000DEEC73 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 239F11B02398F48000CF88D6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 23DC26CE21B80F5A0012243A /* DLGPlayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DLGPlayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 23DC26D221B80F5A0012243A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 23DC291421B810B40012243A /* DLGPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerViewController.h; sourceTree = ""; }; + 23DC291621B810B40012243A /* DLGPlayerDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerDecoder.m; sourceTree = ""; }; + 23DC291721B810B40012243A /* DLGPlayerAudioManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerAudioManager.m; sourceTree = ""; }; + 23DC291821B810B40012243A /* DLGPlayerDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerDecoder.h; sourceTree = ""; }; + 23DC291921B810B40012243A /* DLGPlayerAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerAudioManager.h; sourceTree = ""; }; + 23DC291A21B810B50012243A /* DLGPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayer.h; sourceTree = ""; }; + 23DC291B21B810B50012243A /* DLGPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerViewController.m; sourceTree = ""; }; + 23DC291C21B810B50012243A /* DLGPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayer.m; sourceTree = ""; }; + 23DC291F21B810B50012243A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/DLGPlayerStrings.strings"; sourceTree = ""; }; + 23DC292021B810B50012243A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/DLGPlayerStrings.strings; sourceTree = ""; }; + 23DC292121B810B50012243A /* DLGPlayerRotationScaleVertexShader.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DLGPlayerRotationScaleVertexShader.glsl; sourceTree = ""; }; + 23DC292221B810B50012243A /* DLGPlayerYUVFragmentShader.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DLGPlayerYUVFragmentShader.glsl; sourceTree = ""; }; + 23DC292321B810B50012243A /* DLGPlayerRGBFragmentShader.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DLGPlayerRGBFragmentShader.glsl; sourceTree = ""; }; + 23DC292421B810B50012243A /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/DLGPlayerStrings.strings; sourceTree = ""; }; + 23DC292521B810B50012243A /* DLGPlayerRotationVertexShader.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DLGPlayerRotationVertexShader.glsl; sourceTree = ""; }; + 23DC292621B810B50012243A /* DLGPlayerVertexShader.glsl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DLGPlayerVertexShader.glsl; sourceTree = ""; }; + 23DC292821B810B50012243A /* DLGPlayerVideoYUVFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerVideoYUVFrame.h; sourceTree = ""; }; + 23DC292921B810B50012243A /* DLGPlayerVideoFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerVideoFrame.h; sourceTree = ""; }; + 23DC292A21B810B50012243A /* DLGPlayerVideoRGBFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerVideoRGBFrame.m; sourceTree = ""; }; + 23DC292B21B810B50012243A /* DLGPlayerAudioFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerAudioFrame.m; sourceTree = ""; }; + 23DC292C21B810B50012243A /* DLGPlayerFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerFrame.h; sourceTree = ""; }; + 23DC292D21B810B50012243A /* DLGPlayerVideoRGBFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerVideoRGBFrame.h; sourceTree = ""; }; + 23DC292E21B810B50012243A /* DLGPlayerVideoFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerVideoFrame.m; sourceTree = ""; }; + 23DC292F21B810B50012243A /* DLGPlayerVideoYUVFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerVideoYUVFrame.m; sourceTree = ""; }; + 23DC293021B810B50012243A /* DLGPlayerAudioFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerAudioFrame.h; sourceTree = ""; }; + 23DC293121B810B50012243A /* DLGPlayerFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerFrame.m; sourceTree = ""; }; + 23DC293321B810B50012243A /* DLGPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerView.m; sourceTree = ""; }; + 23DC293421B810B50012243A /* DLGPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerView.h; sourceTree = ""; }; + 23DC293621B810B50012243A /* DLGPlayerDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerDef.h; sourceTree = ""; }; + 23DC293721B810B50012243A /* DLGPlayerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerUtils.h; sourceTree = ""; }; + 23DC293821B810B50012243A /* DLGPlayerUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DLGPlayerUtils.m; sourceTree = ""; }; + 23DC295E21B814470012243A /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 23DC296021B8144D0012243A /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + 23DC296221B814520012243A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 23DC296421B814560012243A /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + 23DC296621B8145B0012243A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + 23DC296821B8145F0012243A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 23DC296A21B8146D0012243A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 23DC296C21B814730012243A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + 23DC296E21B8147A0012243A /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; + 23DC297021B814830012243A /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 23DC297221B8148B0012243A /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + 23E013A5230E7B5B00B9BB0E /* Shader.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Shader.metal; sourceTree = ""; }; + 23E013A7230E7B6B00B9BB0E /* DLGPlayerVideoFrameView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DLGPlayerVideoFrameView.h; sourceTree = ""; }; + 23E013A8230E7B6B00B9BB0E /* MetalPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MetalPlayerView.h; sourceTree = ""; }; + 23E013A9230E7B6B00B9BB0E /* MetalPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MetalPlayerView.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 239BC96B21C234CF00DEEC73 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 239BC99121C2362100DEEC73 /* DLGPlayer.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23DC26CB21B80F5A0012243A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 239BC96F21C234CF00DEEC73 /* DLGPlayerDemo */ = { + isa = PBXGroup; + children = ( + 239BC97C21C234D000DEEC73 /* Info.plist */, + 239BC97021C234CF00DEEC73 /* AppDelegate.swift */, + 239BC97221C234CF00DEEC73 /* RootViewController.swift */, + 23241CAD22A0B45400871C58 /* SecondViewController.swift */, + 239BC97721C234D000DEEC73 /* Assets.xcassets */, + 239BC97921C234D000DEEC73 /* LaunchScreen.storyboard */, + 239BC97421C234CF00DEEC73 /* Main.storyboard */, + 239F11B02398F48000CF88D6 /* ViewController.swift */, + ); + path = DLGPlayerDemo; + sourceTree = ""; + }; + 23DC26C421B80F5A0012243A = { + isa = PBXGroup; + children = ( + 23DC26D021B80F5A0012243A /* DLGPlayer */, + 239BC96F21C234CF00DEEC73 /* DLGPlayerDemo */, + 23DC26CF21B80F5A0012243A /* Products */, + 23DC295D21B814470012243A /* Frameworks */, + ); + sourceTree = ""; + }; + 23DC26CF21B80F5A0012243A /* Products */ = { + isa = PBXGroup; + children = ( + 23DC26CE21B80F5A0012243A /* DLGPlayer.framework */, + 239BC96E21C234CF00DEEC73 /* DLGPlayerDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 23DC26D021B80F5A0012243A /* DLGPlayer */ = { + isa = PBXGroup; + children = ( + 237758B221CA5658000B6B7F /* DLGPlayerControlStatus.h */, + 237758B321CA5658000B6B7F /* DLGPlayerControlStatus.m */, + 23DC291A21B810B50012243A /* DLGPlayer.h */, + 23DC291C21B810B50012243A /* DLGPlayer.m */, + 23DC291421B810B40012243A /* DLGPlayerViewController.h */, + 23DC291B21B810B50012243A /* DLGPlayerViewController.m */, + 235409C421BA177A00533B9B /* DLGSimplePlayerViewController.h */, + 235409C521BA177A00533B9B /* DLGSimplePlayerViewController.m */, + 23DC26D221B80F5A0012243A /* Info.plist */, + 23DC291521B810B40012243A /* codec */, + 23DC293521B810B50012243A /* common */, + 23DC292721B810B50012243A /* frame */, + 23DC291D21B810B50012243A /* resource */, + 23DC293221B810B50012243A /* view */, + ); + path = DLGPlayer; + sourceTree = ""; + }; + 23DC291521B810B40012243A /* codec */ = { + isa = PBXGroup; + children = ( + 23DC291921B810B40012243A /* DLGPlayerAudioManager.h */, + 23DC291721B810B40012243A /* DLGPlayerAudioManager.m */, + 23DC291821B810B40012243A /* DLGPlayerDecoder.h */, + 23DC291621B810B40012243A /* DLGPlayerDecoder.m */, + ); + path = codec; + sourceTree = ""; + }; + 23DC291D21B810B50012243A /* resource */ = { + isa = PBXGroup; + children = ( + 23DC291E21B810B50012243A /* DLGPlayerStrings.strings */, + 23DC292121B810B50012243A /* DLGPlayerRotationScaleVertexShader.glsl */, + 23DC292221B810B50012243A /* DLGPlayerYUVFragmentShader.glsl */, + 23DC292321B810B50012243A /* DLGPlayerRGBFragmentShader.glsl */, + 23DC292521B810B50012243A /* DLGPlayerRotationVertexShader.glsl */, + 23DC292621B810B50012243A /* DLGPlayerVertexShader.glsl */, + 23E013A5230E7B5B00B9BB0E /* Shader.metal */, + ); + path = resource; + sourceTree = ""; + }; + 23DC292721B810B50012243A /* frame */ = { + isa = PBXGroup; + children = ( + 23DC293021B810B50012243A /* DLGPlayerAudioFrame.h */, + 23DC292B21B810B50012243A /* DLGPlayerAudioFrame.m */, + 23DC292C21B810B50012243A /* DLGPlayerFrame.h */, + 23DC293121B810B50012243A /* DLGPlayerFrame.m */, + 23DC292921B810B50012243A /* DLGPlayerVideoFrame.h */, + 23DC292E21B810B50012243A /* DLGPlayerVideoFrame.m */, + 23DC292D21B810B50012243A /* DLGPlayerVideoRGBFrame.h */, + 23DC292A21B810B50012243A /* DLGPlayerVideoRGBFrame.m */, + 23DC292821B810B50012243A /* DLGPlayerVideoYUVFrame.h */, + 23DC292F21B810B50012243A /* DLGPlayerVideoYUVFrame.m */, + ); + path = frame; + sourceTree = ""; + }; + 23DC293221B810B50012243A /* view */ = { + isa = PBXGroup; + children = ( + 23E013A7230E7B6B00B9BB0E /* DLGPlayerVideoFrameView.h */, + 23DC293421B810B50012243A /* DLGPlayerView.h */, + 23DC293321B810B50012243A /* DLGPlayerView.m */, + 23E013A8230E7B6B00B9BB0E /* MetalPlayerView.h */, + 23E013A9230E7B6B00B9BB0E /* MetalPlayerView.m */, + ); + path = view; + sourceTree = ""; + }; + 23DC293521B810B50012243A /* common */ = { + isa = PBXGroup; + children = ( + 23DC293621B810B50012243A /* DLGPlayerDef.h */, + 23DC293721B810B50012243A /* DLGPlayerUtils.h */, + 23DC293821B810B50012243A /* DLGPlayerUtils.m */, + ); + path = common; + sourceTree = ""; + }; + 23DC295D21B814470012243A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 23DC297221B8148B0012243A /* libiconv.tbd */, + 23DC297021B814830012243A /* libz.tbd */, + 23DC296E21B8147A0012243A /* libbz2.tbd */, + 23DC296C21B814730012243A /* CoreAudio.framework */, + 23DC296A21B8146D0012243A /* CoreGraphics.framework */, + 23DC296821B8145F0012243A /* AudioToolbox.framework */, + 23DC296621B8145B0012243A /* MediaPlayer.framework */, + 23DC296421B814560012243A /* CoreMedia.framework */, + 23DC296221B814520012243A /* QuartzCore.framework */, + 23DC296021B8144D0012243A /* OpenGLES.framework */, + 23DC295E21B814470012243A /* AVFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 23DC26C921B80F5A0012243A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 23DC293E21B810B50012243A /* DLGPlayer.h in Headers */, + 237758B421CA5658000B6B7F /* DLGPlayerControlStatus.h in Headers */, + 23E013AB230E7B6B00B9BB0E /* MetalPlayerView.h in Headers */, + 235409BA21BA05B500533B9B /* DLGPlayerFrame.h in Headers */, + 235409BC21BA05B500533B9B /* DLGPlayerAudioFrame.h in Headers */, + 235409B921BA05B500533B9B /* DLGPlayerVideoFrame.h in Headers */, + 235409B821BA05B500533B9B /* DLGPlayerVideoYUVFrame.h in Headers */, + 235409B521BA05A400533B9B /* DLGPlayerAudioManager.h in Headers */, + 235409B421BA05A100533B9B /* DLGPlayerDecoder.h in Headers */, + 235409BD21BA05BC00533B9B /* DLGPlayerView.h in Headers */, + 235409B621BA05A900533B9B /* DLGPlayerDef.h in Headers */, + 235409B721BA05A900533B9B /* DLGPlayerUtils.h in Headers */, + 23E013AA230E7B6B00B9BB0E /* DLGPlayerVideoFrameView.h in Headers */, + 235409C621BA177A00533B9B /* DLGSimplePlayerViewController.h in Headers */, + 235409BB21BA05B500533B9B /* DLGPlayerVideoRGBFrame.h in Headers */, + 23DC293921B810B50012243A /* DLGPlayerViewController.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 239BC96D21C234CF00DEEC73 /* DLGPlayerDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 239BC98C21C234D000DEEC73 /* Build configuration list for PBXNativeTarget "DLGPlayerDemo" */; + buildPhases = ( + 239BC96A21C234CF00DEEC73 /* Sources */, + 239BC96B21C234CF00DEEC73 /* Frameworks */, + 239BC96C21C234CF00DEEC73 /* Resources */, + 239BC99221C2456B00DEEC73 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DLGPlayerDemo; + productName = DLGPlayerDemo; + productReference = 239BC96E21C234CF00DEEC73 /* DLGPlayerDemo.app */; + productType = "com.apple.product-type.application"; + }; + 23DC26CD21B80F5A0012243A /* DLGPlayer */ = { + isa = PBXNativeTarget; + buildConfigurationList = 23DC26E221B80F5A0012243A /* Build configuration list for PBXNativeTarget "DLGPlayer" */; + buildPhases = ( + 236E380F21B8AA76009C6879 /* Run Script */, + 23DC26C921B80F5A0012243A /* Headers */, + 23DC26CA21B80F5A0012243A /* Sources */, + 23DC26CB21B80F5A0012243A /* Frameworks */, + 23DC26CC21B80F5A0012243A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DLGPlayer; + productName = DLGPlayer; + productReference = 23DC26CE21B80F5A0012243A /* DLGPlayer.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 23DC26C521B80F5A0012243A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = "KWANG HYOUN KIM"; + TargetAttributes = { + 239BC96D21C234CF00DEEC73 = { + CreatedOnToolsVersion = 10.1; + }; + 23DC26CD21B80F5A0012243A = { + CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1010; + }; + }; + }; + buildConfigurationList = 23DC26C821B80F5A0012243A /* Build configuration list for PBXProject "DLGPlayer" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + "zh-Hans", + Base, + ); + mainGroup = 23DC26C421B80F5A0012243A; + productRefGroup = 23DC26CF21B80F5A0012243A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 23DC26CD21B80F5A0012243A /* DLGPlayer */, + 239BC96D21C234CF00DEEC73 /* DLGPlayerDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 239BC96C21C234CF00DEEC73 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 239BC97B21C234D000DEEC73 /* LaunchScreen.storyboard in Resources */, + 239BC97821C234D000DEEC73 /* Assets.xcassets in Resources */, + 239BC97621C234CF00DEEC73 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23DC26CC21B80F5A0012243A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23DC294221B810B50012243A /* DLGPlayerRotationScaleVertexShader.glsl in Resources */, + 23DC294521B810B50012243A /* DLGPlayerRotationVertexShader.glsl in Resources */, + 23DC294421B810B50012243A /* DLGPlayerRGBFragmentShader.glsl in Resources */, + 23DC294621B810B50012243A /* DLGPlayerVertexShader.glsl in Resources */, + 23DC294121B810B50012243A /* DLGPlayerStrings.strings in Resources */, + 23DC294321B810B50012243A /* DLGPlayerYUVFragmentShader.glsl in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 236E380F21B8AA76009C6879 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd libs\nsh build.sh\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 239BC96A21C234CF00DEEC73 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23241CAE22A0B45400871C58 /* SecondViewController.swift in Sources */, + 239BC97321C234CF00DEEC73 /* RootViewController.swift in Sources */, + 239F11B12398F48000CF88D6 /* ViewController.swift in Sources */, + 239BC97121C234CF00DEEC73 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 23DC26CA21B80F5A0012243A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 23DC293A21B810B50012243A /* DLGPlayerDecoder.m in Sources */, + 23DC293B21B810B50012243A /* DLGPlayerAudioManager.m in Sources */, + 23DC294021B810B50012243A /* DLGPlayer.m in Sources */, + 235409C721BA177A00533B9B /* DLGSimplePlayerViewController.m in Sources */, + 23DC294D21B810B50012243A /* DLGPlayerVideoFrame.m in Sources */, + 23DC295521B810B50012243A /* DLGPlayerUtils.m in Sources */, + 237758B521CA5658000B6B7F /* DLGPlayerControlStatus.m in Sources */, + 23DC295021B810B50012243A /* DLGPlayerFrame.m in Sources */, + 23E013AC230E7B6B00B9BB0E /* MetalPlayerView.m in Sources */, + 23DC294A21B810B50012243A /* DLGPlayerAudioFrame.m in Sources */, + 23DC294E21B810B50012243A /* DLGPlayerVideoYUVFrame.m in Sources */, + 23DC295121B810B50012243A /* DLGPlayerView.m in Sources */, + 23DC293F21B810B50012243A /* DLGPlayerViewController.m in Sources */, + 23E013A6230E7B5D00B9BB0E /* Shader.metal in Sources */, + 23DC294921B810B50012243A /* DLGPlayerVideoRGBFrame.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 239BC97421C234CF00DEEC73 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 239BC97521C234CF00DEEC73 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 239BC97921C234D000DEEC73 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 239BC97A21C234D000DEEC73 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 23DC291E21B810B50012243A /* DLGPlayerStrings.strings */ = { + isa = PBXVariantGroup; + children = ( + 23DC291F21B810B50012243A /* zh-Hans */, + 23DC292021B810B50012243A /* en */, + 23DC292421B810B50012243A /* Base */, + ); + name = DLGPlayerStrings.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 239BC98821C234D000DEEC73 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = F5NU2PA3PK; + ENABLE_BITCODE = NO; + HEADER_SEARCH_PATHS = ""; + INFOPLIST_FILE = DLGPlayerDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = pisces.lib.DLGPlayerDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "${PRODUCT_NAME}/DLGPlayerDemo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 239BC98921C234D000DEEC73 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = F5NU2PA3PK; + ENABLE_BITCODE = NO; + HEADER_SEARCH_PATHS = ""; + INFOPLIST_FILE = DLGPlayerDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = pisces.lib.DLGPlayerDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_BRIDGING_HEADER = "${PRODUCT_NAME}/DLGPlayerDemo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 23DC26E021B80F5A0012243A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 23DC26E121B80F5A0012243A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 23DC26E321B80F5A0012243A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_BITCODE = NO; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/libs/build/universal/include"; + INFOPLIST_FILE = DLGPlayer/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/build/universal/lib", + ); + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-l\"speex\"", + "-l\"avcodec\"", + "-l\"avdevice\"", + "-l\"avfilter\"", + "-l\"avformat\"", + "-l\"avutil\"", + "-l\"bz2\"", + "-l\"iconv\"", + "-l\"swresample\"", + "-l\"swscale\"", + "-l\"z\"", + "-framework", + "\"OpenGLES\"", + "-framework", + "\"AVFoundation\"", + "-framework", + "\"Accelerate\"", + "-framework", + "\"AudioToolbox\"", + "-framework", + "\"CoreAudio\"", + "-framework", + "\"CoreGraphics\"", + "-framework", + "\"CoreMedia\"", + "-framework", + "\"MediaPlayer\"", + "-framework", + "\"QuartzCore\"", + "-framework", + "\"VideoToolbox\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = carthage.DLGPlayer; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 23DC26E421B80F5A0012243A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_BITCODE = NO; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/libs/build/universal/include"; + INFOPLIST_FILE = DLGPlayer/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/build/universal/lib", + ); + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-l\"speex\"", + "-l\"avcodec\"", + "-l\"avdevice\"", + "-l\"avfilter\"", + "-l\"avformat\"", + "-l\"avutil\"", + "-l\"bz2\"", + "-l\"iconv\"", + "-l\"swresample\"", + "-l\"swscale\"", + "-l\"z\"", + "-framework", + "\"OpenGLES\"", + "-framework", + "\"AVFoundation\"", + "-framework", + "\"Accelerate\"", + "-framework", + "\"AudioToolbox\"", + "-framework", + "\"CoreAudio\"", + "-framework", + "\"CoreGraphics\"", + "-framework", + "\"CoreMedia\"", + "-framework", + "\"MediaPlayer\"", + "-framework", + "\"QuartzCore\"", + "-framework", + "\"VideoToolbox\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = carthage.DLGPlayer; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 239BC98C21C234D000DEEC73 /* Build configuration list for PBXNativeTarget "DLGPlayerDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 239BC98821C234D000DEEC73 /* Debug */, + 239BC98921C234D000DEEC73 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23DC26C821B80F5A0012243A /* Build configuration list for PBXProject "DLGPlayer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23DC26E021B80F5A0012243A /* Debug */, + 23DC26E121B80F5A0012243A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 23DC26E221B80F5A0012243A /* Build configuration list for PBXNativeTarget "DLGPlayer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 23DC26E321B80F5A0012243A /* Debug */, + 23DC26E421B80F5A0012243A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 23DC26C521B80F5A0012243A /* Project object */; +} diff --git a/DLGPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DLGPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..8392ea7 --- /dev/null +++ b/DLGPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme b/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme new file mode 100644 index 0000000..a178d30 --- /dev/null +++ b/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayerDemo.xcscheme b/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayerDemo.xcscheme new file mode 100644 index 0000000..7ca084c --- /dev/null +++ b/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayerDemo.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DLGPlayer/DLGPlayer.h b/DLGPlayer/DLGPlayer.h index d89d502..7b0d541 100644 --- a/DLGPlayer/DLGPlayer.h +++ b/DLGPlayer/DLGPlayer.h @@ -7,26 +7,45 @@ // #import +#import "DLGPlayerAudioFrame.h" +#import "DLGPlayerAudioManager.h" +#import "DLGPlayerControlStatus.h" #import "DLGPlayerDef.h" +#import "DLGPlayerDecoder.h" +#import "DLGPlayerFrame.h" +#import "DLGPlayerUtils.h" +#import "DLGPlayerVideoFrame.h" +#import "DLGPlayerVideoRGBFrame.h" +#import "DLGPlayerVideoYUVFrame.h" +#import "DLGPlayerViewController.h" +#import "DLGSimplePlayerViewController.h" typedef void (^onPauseComplete)(void); @interface DLGPlayer : NSObject - -@property (readonly, strong) UIView *playerView; - -@property (nonatomic) double minBufferDuration; -@property (nonatomic) double maxBufferDuration; +@property (nonatomic) BOOL allowsFrameDrop; +@property (nonatomic) BOOL keepLastFrame; +@property (nonatomic) BOOL mute; +@property (atomic) BOOL playing; +@property (atomic) BOOL buffering; +@property (atomic) BOOL opened; +@property (nonatomic) float brightness; +@property (atomic) double frameDropDuration; +@property (atomic) double minBufferDuration; +@property (atomic) double maxBufferDuration; @property (nonatomic) double position; @property (nonatomic) double duration; -@property (nonatomic) BOOL opened; -@property (nonatomic) BOOL playing; -@property (nonatomic) BOOL buffering; +@property (nonatomic) double speed; @property (nonatomic, strong) NSDictionary *metadata; +@property (nonatomic, readonly) UIView *playerView; +@property (nonatomic, readonly) DLGPlayerAudioManager *audio; -- (void)open:(NSString *)url; +- (void)clearView; - (void)close; +- (void)closeAudio; +- (void)closeCompletely; +- (void)open:(NSString *)url; - (void)play; - (void)pause; - +- (UIImage *)snapshot; @end diff --git a/DLGPlayer/DLGPlayer.m b/DLGPlayer/DLGPlayer.m index 7a9ea5d..962863a 100644 --- a/DLGPlayer/DLGPlayer.m +++ b/DLGPlayer/DLGPlayer.m @@ -14,358 +14,527 @@ #import "DLGPlayerFrame.h" #import "DLGPlayerVideoFrame.h" #import "DLGPlayerAudioFrame.h" +#import "DLGPlayerVideoFrameView.h" +#import "MetalPlayerView.h" -@interface DLGPlayer () - -@property (nonatomic, strong) DLGPlayerView *view; -@property (nonatomic, strong) DLGPlayerDecoder *decoder; -@property (nonatomic, strong) DLGPlayerAudioManager *audio; - -@property (nonatomic, strong) NSMutableArray *vframes; -@property (nonatomic, strong) NSMutableArray *aframes; -@property (nonatomic, strong) DLGPlayerAudioFrame *playingAudioFrame; -@property (nonatomic) NSUInteger playingAudioFrameDataPosition; -@property (nonatomic) double bufferedDuration; -@property (nonatomic) double mediaPosition; -@property (nonatomic) double mediaSyncTime; -@property (nonatomic) double mediaSyncPosition; +#define DLGPlayerFrameDropLimit 0.4 -@property (nonatomic, strong) NSThread *frameReaderThread; +@interface DLGPlayer () +@property (atomic) BOOL closing; +@property (atomic) BOOL opening; +@property (atomic) BOOL frameDropEnabled; @property (nonatomic) BOOL notifiedBufferStart; +@property (nonatomic) BOOL renderBegan; @property (nonatomic) BOOL requestSeek; +@property (atomic) double bufferedDuration; +@property (nonatomic) double mediaPosition; +@property (nonatomic) double mediaSyncPosition; +@property (nonatomic) double mediaSyncTime; @property (nonatomic) double requestSeekPosition; -@property (nonatomic) BOOL opening; - +@property (nonatomic) NSUInteger playingAudioFrameDataPosition; +@property (nonatomic, strong) dispatch_queue_t frameReaderQueue; +@property (nonatomic, strong) dispatch_queue_t processingQueue; +@property (nonatomic, strong) dispatch_queue_t renderingQueue; @property (nonatomic, strong) dispatch_semaphore_t vFramesLock; @property (nonatomic, strong) dispatch_semaphore_t aFramesLock; - +@property (nonatomic, strong) NSMutableArray *vframes; +@property (nonatomic, strong) NSMutableArray *aframes; +@property (nonatomic, strong) DLGPlayerAudioFrame *playingAudioFrame; +@property (nonatomic, strong) DLGPlayerDecoder *decoder; +@property (nonatomic, strong) DLGPlayerAudioManager *audio; +@property (nonatomic, strong) id view; @end @implementation DLGPlayer -- (id)init { - self = [super init]; - if (self) { - [self initAll]; - } - return self; +#pragma mark - Public Properties + +- (UIView *)playerView { + return (UIView *) _view; } -- (void)dealloc { - NSLog(@"DLGPlayer dealloc"); +- (void)setPosition:(double)position { + _requestSeekPosition = position; + _requestSeek = YES; } -- (void)initAll { - [self initVars]; - [self initAudio]; - [self initDecoder]; - [self initView]; +- (double)position { + return _mediaPosition; } -- (void)initVars { - self.minBufferDuration = DLGPlayerMinBufferDuration; - self.maxBufferDuration = DLGPlayerMaxBufferDuration; - self.bufferedDuration = 0; - self.mediaPosition = 0; - self.mediaSyncTime = 0; - self.vframes = [NSMutableArray arrayWithCapacity:128]; - self.aframes = [NSMutableArray arrayWithCapacity:128]; - self.playingAudioFrame = nil; - self.playingAudioFrameDataPosition = 0; - self.opening = NO; - self.buffering = NO; - self.playing = NO; - self.opened = NO; - self.requestSeek = NO; - self.requestSeekPosition = 0; - self.frameReaderThread = nil; - self.aFramesLock = dispatch_semaphore_create(1); - self.vFramesLock = dispatch_semaphore_create(1); +- (void)setSpeed:(double)speed { + _speed = speed; + _decoder.speed = speed; } -- (void)initView { - DLGPlayerView *v = [[DLGPlayerView alloc] init]; - self.view = v; +- (void)setMute:(BOOL)mute { + if (mute == _mute) { + return; + } + + _mute = mute; + _audio.mute = mute; + + __weak typeof(self)weakSelf = self; + + dispatch_async(_processingQueue, ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (!strongSelf) { + return; + } + + if (mute) { + if ([strongSelf.audio close]) { + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationAudioClosed object:strongSelf]; + } + } else if (strongSelf.playing) { + if (!strongSelf.audio.opened && [strongSelf.audio open]) { + strongSelf.decoder.audioChannels = [strongSelf.audio channels]; + strongSelf.decoder.audioSampleRate = [strongSelf.audio sampleRate]; + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationAudioOpened object:strongSelf]; + } + [strongSelf.audio play]; + } + + strongSelf.decoder.mute = mute; + }); } -- (void)initDecoder { - self.decoder = [[DLGPlayerDecoder alloc] init]; +#pragma mark - Con(De)structors + +- (id)init { + self = [super init]; + if (self) { + [self initAll]; + } + return self; } -- (void)initAudio { - self.audio = [[DLGPlayerAudioManager alloc] init]; +- (void)dealloc { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"DLGPlayer dealloc"); + } } -- (void)clearVars { - [self.vframes removeAllObjects]; - [self.aframes removeAllObjects]; - self.playingAudioFrame = nil; - self.playingAudioFrameDataPosition = 0; - self.opening = NO; - self.buffering = NO; - self.playing = NO; - self.opened = NO; - self.bufferedDuration = 0; - self.mediaPosition = 0; - self.mediaSyncTime = 0; - [self.view clear]; +#pragma mark - Public Methods + +- (void)clearView { + [_view clear:YES]; } - (void)open:(NSString *)url { __weak typeof(self)weakSelf = self; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + + dispatch_async(_processingQueue, ^{ __strong typeof(weakSelf)strongSelf = weakSelf; - if (!strongSelf) { + + if (!strongSelf || strongSelf.opening || strongSelf.closing) { return; } - - NSError *error = nil; + strongSelf.opening = YES; - if ([strongSelf.audio open:&error]) { + if (!strongSelf.audio.opened && [strongSelf.audio open]) { strongSelf.decoder.audioChannels = [strongSelf.audio channels]; strongSelf.decoder.audioSampleRate = [strongSelf.audio sampleRate]; - } else { - [strongSelf handleError:error]; - } - - if (![strongSelf.decoder open:url error:&error]) { - strongSelf.opening = NO; - [strongSelf handleError:error]; - return; + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationAudioOpened object:strongSelf]; } - dispatch_async(dispatch_get_main_queue(), ^{ - strongSelf.view.isYUV = [strongSelf.decoder isYUV]; - strongSelf.view.keepLastFrame = [strongSelf.decoder hasPicture] && ![strongSelf.decoder hasVideo]; - strongSelf.view.rotation = strongSelf.decoder.rotation; - strongSelf.view.contentSize = CGSizeMake([strongSelf.decoder videoWidth], [strongSelf.decoder videoHeight]); - strongSelf.view.contentMode = UIViewContentModeScaleAspectFit; - - strongSelf.duration = strongSelf.decoder.duration; - strongSelf.metadata = strongSelf.decoder.metadata; - strongSelf.opening = NO; - strongSelf.buffering = NO; - strongSelf.playing = NO; - strongSelf.bufferedDuration = 0; - strongSelf.mediaPosition = 0; - strongSelf.mediaSyncTime = 0; - - __weak typeof(strongSelf)ws = strongSelf; - strongSelf.audio.frameReaderBlock = ^(float *data, UInt32 frames, UInt32 channels) { - [ws readAudioFrame:data frames:frames channels:channels]; - }; - - strongSelf.opened = YES; - [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationOpened object:strongSelf]; + + dispatch_async(strongSelf.frameReaderQueue, ^{ + NSError *error = nil; + if (![strongSelf.decoder open:url error:&error]) { + strongSelf.opening = NO; + [strongSelf handleError:error]; + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if ([strongSelf.view isKindOfClass:[DLGPlayerView class]]) { + DLGPlayerView *view = (DLGPlayerView *) strongSelf.view; + [view setCurrentEAGLContext]; + } + + strongSelf.view.isYUV = strongSelf.decoder.isYUV; + strongSelf.view.keepLastFrame = strongSelf.keepLastFrame && strongSelf.decoder.hasPicture && !strongSelf.decoder.hasVideo; + strongSelf.view.rotation = strongSelf.decoder.rotation; + strongSelf.view.contentSize = CGSizeMake(strongSelf.decoder.videoWidth, strongSelf.decoder.videoHeight); + + if ([strongSelf.view isKindOfClass:[UIView class]]) { + ((UIView *) strongSelf.view).contentMode = UIViewContentModeScaleToFill; + } + + strongSelf.duration = strongSelf.decoder.duration; + strongSelf.metadata = strongSelf.decoder.metadata; + strongSelf.opening = NO; + strongSelf.buffering = NO; + strongSelf.playing = NO; + strongSelf.bufferedDuration = 0; + strongSelf.mediaPosition = 0; + strongSelf.mediaSyncTime = 0; + + __weak typeof(strongSelf)ws = strongSelf; + strongSelf.audio.frameReaderBlock = ^(float *data, UInt32 frames, UInt32 channels) { + [ws readAudioFrame:data frames:frames channels:channels]; + }; + + strongSelf.opened = YES; + + if (strongSelf.allowsFrameDrop) { + strongSelf.frameDropEnabled = YES; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationOpened object:strongSelf]; + }); }); }); } - (void)close { - if (!self.opened && !self.opening) { - [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationClosed object:self]; - return; - } + [self close:NO]; +} - [self pause]; - [self.decoder prepareClose]; +- (void)closeAudio { + __weak typeof(self)weakSelf = self; + + dispatch_async(_processingQueue, ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (strongSelf && [strongSelf.audio close]) { + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationAudioClosed object:strongSelf]; + } + }); +} - dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC); +- (void)closeCompletely { + [self close:YES]; +} +- (void)play { __weak typeof(self)weakSelf = self; - - dispatch_source_set_event_handler(timer, ^{ + + dispatch_async(_processingQueue, ^{ __strong typeof(weakSelf)strongSelf = weakSelf; - if (!strongSelf) { + + if (!strongSelf || !strongSelf.opened || strongSelf.playing || strongSelf.closing) { return; } + + strongSelf.playing = YES; - if (strongSelf.opening || strongSelf.buffering) return; - [strongSelf.decoder close]; - - NSArray *errors = nil; - if ([strongSelf.audio close:&errors]) { - [strongSelf clearVars]; - [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationClosed object:strongSelf]; - } else { - for (NSError *error in errors) { - [strongSelf handleError:error]; - } - } - dispatch_cancel(timer); + [strongSelf.audio play]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [strongSelf render]; + }); + + dispatch_async(strongSelf.frameReaderQueue, ^{ + [strongSelf runFrameReader]; + }); }); - dispatch_resume(timer); } -- (void)play { - if (!self.opened || self.playing) return; - - self.playing = YES; +- (void)pause { __weak typeof(self)weakSelf = self; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + dispatch_async(_processingQueue, ^{ __strong typeof(weakSelf)strongSelf = weakSelf; - if (!strongSelf) { - return; + + if (strongSelf.playing) { + strongSelf.playing = NO; + + [strongSelf.audio pause]; } - - [strongSelf render]; - [strongSelf startFrameReaderThread]; }); +} - NSError *error = nil; - if (![self.audio play:&error]) { - [self handleError:error]; - } +- (UIImage *)snapshot { + return [_view snapshot]; } -- (void)pause { - self.playing = NO; - NSError *error = nil; +#pragma mark - Private Methods + +- (void)initAll { + [self initVars]; + [self initAudio]; + [self initDecoder]; + [self initView]; +} - if (![self.audio pause:&error]) { - [self handleError:error]; +- (void)initVars { + _allowsFrameDrop = NO; + _frameDropEnabled = NO; + _keepLastFrame = YES; + _requestSeek = NO; + _renderBegan = NO; + _buffering = NO; + _closing = NO; + _opening = NO; + _playing = NO; + _opened = NO; + _frameDropDuration = DLGPlayerFrameDropDuration; + _minBufferDuration = DLGPlayerMinBufferDuration; + _maxBufferDuration = DLGPlayerMaxBufferDuration; + _mediaSyncTime = 0; + _brightness = 1; + _requestSeekPosition = 0; + _speed = 1.0; + _bufferedDuration = 0; + _mediaPosition = 0; + _playingAudioFrameDataPosition = 0; + _playingAudioFrame = nil; + + _aFramesLock = dispatch_semaphore_create(1); + _vFramesLock = dispatch_semaphore_create(1); + _vframes = [NSMutableArray arrayWithCapacity:128]; + _aframes = [NSMutableArray arrayWithCapacity:128]; + + const char *frameReaderQueueName = [NSString stringWithFormat:@"DLGPlayer.frameReaderQueue::%zd", self.hash].UTF8String; + const char *processingQueueName = [NSString stringWithFormat:@"DLGPlayer.processingQueue::%zd", self.hash].UTF8String; + const char *renderingQueueName = [NSString stringWithFormat:@"DLGPlayer.renderingQueue::%zd", self.hash].UTF8String; + + if (@available(iOS 10.0, *)) { + _frameReaderQueue = dispatch_queue_create(frameReaderQueueName, DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + _processingQueue = dispatch_queue_create(processingQueueName, DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + _renderingQueue = dispatch_queue_create(renderingQueueName, DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL); + } else { + _frameReaderQueue = dispatch_queue_create(frameReaderQueueName, DISPATCH_QUEUE_SERIAL); + _processingQueue = dispatch_queue_create(processingQueueName, DISPATCH_QUEUE_SERIAL); + _renderingQueue = dispatch_queue_create(renderingQueueName, DISPATCH_QUEUE_SERIAL); } } -- (void)startFrameReaderThread { - if (self.frameReaderThread == nil) { - self.frameReaderThread = [[NSThread alloc] initWithTarget:self selector:@selector(runFrameReader) object:nil]; - [self.frameReaderThread start]; +- (void)initView { + if (@available(iOS 9.0, *)) { + _view = [DLGPlayerUtils isMetalSupport] ? [MetalPlayerView new] : [DLGPlayerView new]; + } else { + _view = [DLGPlayerView new]; } } -- (void)runFrameReader { - @autoreleasepool { - while (self.playing) { - [self readFrame]; - if (self.requestSeek) { - [self seekPositionInFrameReader]; - } else { - [NSThread sleepForTimeInterval:1.5]; +- (void)initDecoder { + _decoder = [[DLGPlayerDecoder alloc] init]; + _decoder.speed = _speed; +} + +- (void)initAudio { + _audio = [[DLGPlayerAudioManager alloc] init]; +} + +- (void)clearVars { + { + dispatch_semaphore_wait(_vFramesLock, DISPATCH_TIME_FOREVER); + [_vframes removeAllObjects]; + dispatch_semaphore_signal(_vFramesLock); + } + { + dispatch_semaphore_wait(_aFramesLock, DISPATCH_TIME_FOREVER); + [_aframes removeAllObjects]; + dispatch_semaphore_signal(_aFramesLock); + } + + _buffering = NO; + _closing = NO; + _frameDropEnabled = NO; + _opened = NO; + _opening = NO; + _playing = NO; + _renderBegan = NO; + _bufferedDuration = 0; + _mediaPosition = 0; + _mediaSyncTime = 0; + _playingAudioFrameDataPosition = 0; + _playingAudioFrame = nil; +} + +- (void)close:(BOOL)completely { + __weak typeof(self)weakSelf = self; + + dispatch_async(_processingQueue, ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (!strongSelf || strongSelf.closing || !strongSelf.opened) { + return; + } + + strongSelf.closing = YES; + strongSelf.playing = NO; + + if (completely) { + if ([strongSelf.audio close]) { + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationAudioClosed object:strongSelf]; } + } else { + [strongSelf.audio pause]; + } + + dispatch_async(strongSelf.frameReaderQueue, ^{ + [strongSelf.decoder prepareClose]; + [strongSelf.decoder close]; + }); + + [strongSelf clearVars]; + [strongSelf.view clear:NO]; + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationClosed object:strongSelf]; + }); +} + +- (void)runFrameReader { + while (_playing && !_closing) { + [self readFrame]; + + if (_requestSeek) { + [self seekPositionInFrameReader]; + } else { + [NSThread sleepForTimeInterval:1.5]; } - self.frameReaderThread = nil; } } - (void)readFrame { - self.buffering = YES; + _buffering = YES; - NSMutableArray *tempVFrames = [NSMutableArray arrayWithCapacity:8]; - NSMutableArray *tempAFrames = [NSMutableArray arrayWithCapacity:8]; double tempDuration = 0; + double droppedDuration = 0; + NSMutableArray *tempVFrames = [NSMutableArray arrayWithCapacity:15]; + NSMutableArray *tempAFrames = [NSMutableArray arrayWithCapacity:15]; dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 0.02 * NSEC_PER_SEC); - while (self.playing && !self.decoder.isEOF && !self.requestSeek - && (self.bufferedDuration + tempDuration) < self.maxBufferDuration) { + if (DLGPlayerUtils.debugEnabled && _frameDropEnabled) { + NSLog(@"DLGPlayer fram drop began!"); + } + + while (_playing && !_closing && !_decoder.isEOF && !_requestSeek + && (_frameDropEnabled || (_bufferedDuration + tempDuration) < _maxBufferDuration)) { @autoreleasepool { - NSArray *fs = [self.decoder readFrames]; + NSArray *fs = [_decoder readFrames]; + if (fs == nil) { break; } if (fs.count == 0) { continue; } - { - for (DLGPlayerFrame *f in fs) { - if (f.type == kDLGPlayerFrameTypeVideo) { - [tempVFrames addObject:f]; - tempDuration += f.duration; + for (DLGPlayerFrame *f in fs) { + if (f.type == kDLGPlayerFrameTypeVideo) { + if (_frameDropEnabled) { + f.dropFrame = YES; + droppedDuration += f.duration; } + + [tempVFrames addObject:f]; + tempDuration += f.duration; } - - long timeout = dispatch_semaphore_wait(self.vFramesLock, t); - if (timeout == 0) { - if (tempVFrames.count > 0) { - self.bufferedDuration += tempDuration; - tempDuration = 0; - [self.vframes addObjectsFromArray:tempVFrames]; - [tempVFrames removeAllObjects]; + + if (!_mute && f.type == kDLGPlayerFrameTypeAudio) { + if (!_decoder.hasVideo) { + if (_frameDropEnabled) { + f.dropFrame = YES; + droppedDuration += f.duration; + } + tempDuration += f.duration; } - dispatch_semaphore_signal(self.vFramesLock); + [tempAFrames addObject:f]; } } - { - for (DLGPlayerFrame *f in fs) { - if (f.type == kDLGPlayerFrameTypeAudio) { - [tempAFrames addObject:f]; - if (!self.decoder.hasVideo) tempDuration += f.duration; - } + + long timeout = dispatch_semaphore_wait(_vFramesLock, t); + if (timeout == 0) { + if (tempVFrames.count > 0) { + _bufferedDuration += tempDuration; + tempDuration = 0; + + [_vframes addObjectsFromArray:tempVFrames]; + [tempVFrames removeAllObjects]; } - - long timeout = dispatch_semaphore_wait(self.aFramesLock, t); + dispatch_semaphore_signal(_vFramesLock); + } + + if (!_mute) { + long timeout = dispatch_semaphore_wait(_aFramesLock, t); if (timeout == 0) { if (tempAFrames.count > 0) { - if (!self.decoder.hasVideo) { - self.bufferedDuration += tempDuration; + if (!_decoder.hasVideo) { + _bufferedDuration += tempDuration; tempDuration = 0; } - [self.aframes addObjectsFromArray:tempAFrames]; + [_aframes addObjectsFromArray:tempAFrames]; [tempAFrames removeAllObjects]; } - dispatch_semaphore_signal(self.aFramesLock); + dispatch_semaphore_signal(_aFramesLock); } } } + + if (DLGPlayerUtils.debugEnabled && _frameDropEnabled) { + NSLog(@"_bufferedDuration -> %f", _bufferedDuration); + } + + if (_frameDropEnabled && droppedDuration > _frameDropDuration) { + _frameDropEnabled = NO; + droppedDuration = 0; + + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"DLGPlayer fram drop ended!"); + } + } } { // add the rest video frames while (tempVFrames.count > 0 || tempAFrames.count > 0) { if (tempVFrames.count > 0) { - long timeout = dispatch_semaphore_wait(self.vFramesLock, t); + long timeout = dispatch_semaphore_wait(_vFramesLock, t); if (timeout == 0) { - self.bufferedDuration += tempDuration; + _bufferedDuration += tempDuration; tempDuration = 0; - [self.vframes addObjectsFromArray:tempVFrames]; + [_vframes addObjectsFromArray:tempVFrames]; [tempVFrames removeAllObjects]; - dispatch_semaphore_signal(self.vFramesLock); + dispatch_semaphore_signal(_vFramesLock); } } if (tempAFrames.count > 0) { - long timeout = dispatch_semaphore_wait(self.aFramesLock, t); + long timeout = dispatch_semaphore_wait(_aFramesLock, t); if (timeout == 0) { - if (!self.decoder.hasVideo) { - self.bufferedDuration += tempDuration; + if (!_decoder.hasVideo) { + _bufferedDuration += tempDuration; tempDuration = 0; } - [self.aframes addObjectsFromArray:tempAFrames]; + [_aframes addObjectsFromArray:tempAFrames]; [tempAFrames removeAllObjects]; - dispatch_semaphore_signal(self.aFramesLock); + dispatch_semaphore_signal(_aFramesLock); } } } } - self.buffering = NO; + _buffering = NO; } - (void)seekPositionInFrameReader { - [self.decoder seek:self.requestSeekPosition]; - + [_decoder seek:_requestSeekPosition]; + { - dispatch_semaphore_wait(self.vFramesLock, DISPATCH_TIME_FOREVER); - [self.vframes removeAllObjects]; - dispatch_semaphore_signal(self.vFramesLock); + dispatch_semaphore_wait(_vFramesLock, DISPATCH_TIME_FOREVER); + [_vframes removeAllObjects]; + dispatch_semaphore_signal(_vFramesLock); } { - dispatch_semaphore_wait(self.aFramesLock, DISPATCH_TIME_FOREVER); - [self.aframes removeAllObjects]; - dispatch_semaphore_signal(self.aFramesLock); + dispatch_semaphore_wait(_aFramesLock, DISPATCH_TIME_FOREVER); + [_aframes removeAllObjects]; + dispatch_semaphore_signal(_aFramesLock); } - - self.bufferedDuration = 0; - self.requestSeek = NO; - self.mediaSyncTime = 0; - self.mediaPosition = self.requestSeekPosition; + + _bufferedDuration = 0; + _requestSeek = NO; + _mediaSyncTime = 0; + _mediaPosition = _requestSeekPosition; } - (void)render { - if (!self.playing) return; - - BOOL eof = self.decoder.isEOF; - BOOL noframes = ((self.decoder.hasVideo && self.vframes.count <= 0) || - (self.decoder.hasAudio && self.aframes.count <= 0)); + if (!_playing) + return; + + BOOL eof = _decoder.isEOF; + BOOL noframes = ((_decoder.hasVideo && _vframes.count <= 0) && + (_decoder.hasAudio && _aframes.count <= 0)); // Check if reach the end and play all frames. if (noframes && eof) { @@ -374,28 +543,33 @@ - (void)render { return; } - if (noframes && !self.notifiedBufferStart) { - self.notifiedBufferStart = YES; - NSDictionary *userInfo = @{ DLGPlayerNotificationBufferStateKey : @(self.notifiedBufferStart) }; + if (noframes && !_notifiedBufferStart) { + _notifiedBufferStart = YES; + NSDictionary *userInfo = @{DLGPlayerNotificationBufferStateKey: @(_notifiedBufferStart)}; [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationBufferStateChanged object:self userInfo:userInfo]; - } else if (!noframes && self.notifiedBufferStart && self.bufferedDuration >= self.minBufferDuration) { - self.notifiedBufferStart = NO; - NSDictionary *userInfo = @{ DLGPlayerNotificationBufferStateKey : @(self.notifiedBufferStart) }; + } else if (!noframes && _notifiedBufferStart && _bufferedDuration >= _minBufferDuration / _speed) { + _notifiedBufferStart = NO; + NSDictionary *userInfo = @{DLGPlayerNotificationBufferStateKey: @(_notifiedBufferStart)}; [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationBufferStateChanged object:self userInfo:userInfo]; } // Render if has picture - if (self.decoder.hasPicture && self.vframes.count > 0) { - DLGPlayerVideoFrame *frame = self.vframes[0]; - self.view.contentSize = CGSizeMake(frame.width, frame.height); - [self.vframes removeObjectAtIndex:0]; - [self.view render:frame]; + if (_decoder.hasPicture && _vframes.count > 0) { + DLGPlayerVideoFrame *frame = _vframes[0]; + frame.brightness = _brightness; + _view.contentSize = CGSizeMake(frame.width, frame.height); + + if (dispatch_semaphore_wait(_vFramesLock, DISPATCH_TIME_NOW) == 0) { + [_vframes removeObjectAtIndex:0]; + dispatch_semaphore_signal(_vFramesLock); + [self renderView:frame]; + } } // Check whether render is neccessary - if (self.vframes.count <= 0 || !self.decoder.hasVideo || self.notifiedBufferStart) { + if (_vframes.count <= 0 || !_decoder.hasVideo || _notifiedBufferStart) { __weak typeof(self)weakSelf = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf render]; }); return; @@ -404,43 +578,67 @@ - (void)render { // Render video DLGPlayerVideoFrame *frame = nil; { - long timeout = dispatch_semaphore_wait(self.vFramesLock, DISPATCH_TIME_NOW); - if (timeout == 0) { - frame = self.vframes[0]; - self.mediaPosition = frame.position; - self.bufferedDuration -= frame.duration; - [self.vframes removeObjectAtIndex:0]; - dispatch_semaphore_signal(self.vFramesLock); + if (dispatch_semaphore_wait(_vFramesLock, DISPATCH_TIME_NOW) == 0) { + frame = _vframes[0]; + frame.brightness = _brightness; + _mediaPosition = frame.position; + _bufferedDuration -= frame.duration; + [_vframes removeObjectAtIndex:0]; + dispatch_semaphore_signal(_vFramesLock); } } - [self.view render:frame]; + + [self renderView:frame]; - // Sync audio with video double syncTime = [self syncTime]; - NSTimeInterval t = MAX(frame.duration + syncTime, 0.01); - + NSTimeInterval t; + if (_speed > 1) { + t = frame.duration; + } else { + t = frame.dropFrame ? 0.01 : MIN(frame.duration, MAX(frame.duration + syncTime, 0.01)); + } + __weak typeof(self)weakSelf = self; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(t * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (t * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [weakSelf render]; }); } +- (void)renderView:(DLGPlayerVideoFrame *)frame { + __weak typeof(self)weakSelf = self; + + dispatch_sync(_renderingQueue, ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (!strongSelf) { + return; + } + + [strongSelf.view render:frame]; + + if (!strongSelf.renderBegan && frame.width > 0 && frame.height > 0) { + strongSelf.renderBegan = YES; + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationRenderBegan object:strongSelf]; + } + }); +} + - (double)syncTime { const double now = [NSDate timeIntervalSinceReferenceDate]; - if (self.mediaSyncTime == 0) { - self.mediaSyncTime = now; - self.mediaSyncPosition = self.mediaPosition; + if (_mediaSyncTime == 0) { + _mediaSyncTime = now; + _mediaSyncPosition = _mediaPosition; return 0; } - double dp = self.mediaPosition - self.mediaSyncPosition; - double dt = now - self.mediaSyncTime; + double dp = _mediaPosition - _mediaSyncPosition; + double dt = now - _mediaSyncTime; double sync = dp - dt; if (sync > 1 || sync < -1) { sync = 0; - self.mediaSyncTime = 0; + _mediaSyncTime = 0; } return sync; @@ -450,49 +648,61 @@ - (double)syncTime { * For audioUnitRenderCallback, (DLGPlayerAudioManagerFrameReaderBlock)readFrameBlock */ - (void)readAudioFrame:(float *)data frames:(UInt32)frames channels:(UInt32)channels { - if (!self.playing) return; - + if (!_playing) { + return; + } + while(frames > 0) { @autoreleasepool { - if (self.playingAudioFrame == nil) { + if (_playingAudioFrame == nil) { { - if (self.aframes.count <= 0) { + if (_aframes.count <= 0) { memset(data, 0, frames * channels * sizeof(float)); return; } - long timeout = dispatch_semaphore_wait(self.aFramesLock, DISPATCH_TIME_NOW); + long timeout = dispatch_semaphore_wait(_aFramesLock, DISPATCH_TIME_NOW); if (timeout == 0) { - DLGPlayerAudioFrame *frame = self.aframes[0]; - if (self.decoder.hasVideo) { - const double dt = self.mediaPosition - frame.position; - if (dt < -0.1) { // audio is faster than video, silence - memset(data, 0, frames * channels * sizeof(float)); - dispatch_semaphore_signal(self.aFramesLock); - break; - } else if (dt > 0.1) { // audio is slower than video, skip - [self.aframes removeObjectAtIndex:0]; - dispatch_semaphore_signal(self.aFramesLock); + @autoreleasepool { + DLGPlayerAudioFrame *frame = _aframes[0]; + + if (frame.dropFrame) { + [_aframes removeObjectAtIndex:0]; + dispatch_semaphore_signal(_aFramesLock); continue; + } + + if (_decoder.hasVideo) { + const double dt = _mediaPosition - frame.position; + + if (dt < -0.1) { // audio is faster than video, silence + memset(data, 0, frames * channels * sizeof(float)); + dispatch_semaphore_signal(_aFramesLock); + break; + } else if (dt > 0.1) { // audio is slower than video, skip + [_aframes removeObjectAtIndex:0]; + dispatch_semaphore_signal(_aFramesLock); + continue; + } else { + _playingAudioFrameDataPosition = 0; + _playingAudioFrame = frame; + [_aframes removeObjectAtIndex:0]; + } } else { - self.playingAudioFrameDataPosition = 0; - self.playingAudioFrame = frame; - [self.aframes removeObjectAtIndex:0]; + _playingAudioFrameDataPosition = 0; + _playingAudioFrame = frame; + [_aframes removeObjectAtIndex:0]; + _mediaPosition = frame.position; + _bufferedDuration -= frame.duration; } - } else { - self.playingAudioFrameDataPosition = 0; - self.playingAudioFrame = frame; - [self.aframes removeObjectAtIndex:0]; - self.mediaPosition = frame.position; - self.bufferedDuration -= frame.duration; } - dispatch_semaphore_signal(self.aFramesLock); + dispatch_semaphore_signal(_aFramesLock); } else return; } } - NSData *frameData = self.playingAudioFrame.data; - NSUInteger pos = self.playingAudioFrameDataPosition; + NSData *frameData = _playingAudioFrame.data; + NSUInteger pos = _playingAudioFrameDataPosition; if (frameData == nil) { memset(data, 0, frames * channels * sizeof(float)); return; @@ -509,31 +719,19 @@ - (void)readAudioFrame:(float *)data frames:(UInt32)frames channels:(UInt32)chan data += framesToCopy * channels; if (bytesToCopy < remainingBytes) { - self.playingAudioFrameDataPosition += bytesToCopy; + _playingAudioFrameDataPosition += bytesToCopy; } else { - self.playingAudioFrame = nil; + _playingAudioFrame = nil; } } } } -- (UIView *)playerView { - return self.view; -} - -- (void)setPosition:(double)position { - self.requestSeekPosition = position; - self.requestSeek = YES; -} - -- (double)position { - return self.mediaPosition; -} - -#pragma mark - Handle Error - (void)handleError:(NSError *)error { - if (error == nil) return; - NSDictionary *userInfo = @{ DLGPlayerNotificationErrorKey : error }; + if (error == nil) { + return; + } + NSDictionary *userInfo = @{DLGPlayerNotificationErrorKey: error}; [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationError object:self userInfo:userInfo]; } diff --git a/DLGPlayer/DLGPlayerControlStatus.h b/DLGPlayer/DLGPlayerControlStatus.h new file mode 100644 index 0000000..a467ec4 --- /dev/null +++ b/DLGPlayer/DLGPlayerControlStatus.h @@ -0,0 +1,16 @@ +// +// DLGPlayerControlStatus.h +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 19/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#import +#import "DLGPlayerDef.h" + +@interface DLGPlayerControlStatus : NSObject +@property (nonatomic) BOOL playing; +- (instancetype)initWithStatus:(DLGPlayerStatus)status; +- (void)setStatus:(DLGPlayerStatus)status; +@end diff --git a/DLGPlayer/DLGPlayerControlStatus.m b/DLGPlayer/DLGPlayerControlStatus.m new file mode 100644 index 0000000..83800ad --- /dev/null +++ b/DLGPlayer/DLGPlayerControlStatus.m @@ -0,0 +1,40 @@ +// +// DLGPlayerControlStatus.m +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 19/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#import "DLGPlayerControlStatus.h" + +@implementation DLGPlayerControlStatus +{ + DLGPlayerStatus _status; +} + +- (instancetype)initWithStatus:(DLGPlayerStatus)status { + self = [super init]; + if (self) { + _status = status; + } + return self; +} + +- (BOOL)playing { + switch (_status) { + case DLGPlayerStatusPaused: + case DLGPlayerStatusClosing: + case DLGPlayerStatusClosed: + case DLGPlayerStatusNone: + return NO; + default: + return YES; + } +} + +- (void)setStatus:(DLGPlayerStatus)status { + _status = status; +} + +@end diff --git a/DLGPlayer/DLGPlayerViewController.h b/DLGPlayer/DLGPlayerViewController.h index a553c13..4d20576 100644 --- a/DLGPlayer/DLGPlayerViewController.h +++ b/DLGPlayer/DLGPlayerViewController.h @@ -9,18 +9,6 @@ #import #import "DLGPlayer.h" -typedef enum : NSUInteger { - DLGPlayerStatusNone, - DLGPlayerStatusOpening, - DLGPlayerStatusOpened, - DLGPlayerStatusPlaying, - DLGPlayerStatusBuffering, - DLGPlayerStatusPaused, - DLGPlayerStatusEOF, - DLGPlayerStatusClosing, - DLGPlayerStatusClosed, -} DLGPlayerStatus; - @interface DLGPlayerViewController : UIViewController @property (nonatomic, copy) NSString *url; diff --git a/DLGPlayer/DLGPlayerViewController.m b/DLGPlayer/DLGPlayerViewController.m index 68d4f3d..b3b5f2f 100644 --- a/DLGPlayer/DLGPlayerViewController.m +++ b/DLGPlayer/DLGPlayerViewController.m @@ -41,7 +41,6 @@ @interface DLGPlayerViewController () { @property (nonatomic) NSTimer *timerForHUD; @property (nonatomic, readwrite) DLGPlayerStatus status; -@property (nonatomic) DLGPlayerOperation nextOperation; @end @@ -99,7 +98,6 @@ - (void)initAll { [self initBuffering]; [self initGestures]; self.status = DLGPlayerStatusNone; - self.nextOperation = DLGPlayerOperationNone; } - (void)onPlayButtonTapped:(id)sender { @@ -148,14 +146,6 @@ - (void)syncHUD:(BOOL)force { } - (void)open { - if (self.status == DLGPlayerStatusClosing) { - self.nextOperation = DLGPlayerOperationOpen; - return; - } - if (self.status != DLGPlayerStatusNone && - self.status != DLGPlayerStatusClosed) { - return; - } self.status = DLGPlayerStatusOpening; self.aivBuffering.hidden = NO; [self.aivBuffering startAnimating]; @@ -163,10 +153,6 @@ - (void)open { } - (void)close { - if (self.status == DLGPlayerStatusOpening) { - self.nextOperation = DLGPlayerOperationClose; - return; - } self.status = DLGPlayerStatusClosing; [UIApplication sharedApplication].idleTimerDisabled = NO; [self.player close]; @@ -174,16 +160,6 @@ - (void)close { } - (void)play { - if (self.status == DLGPlayerStatusNone || - self.status == DLGPlayerStatusClosed) { - [self open]; - self.nextOperation = DLGPlayerOperationPlay; - } - if (self.status != DLGPlayerStatusOpened && - self.status != DLGPlayerStatusPaused && - self.status != DLGPlayerStatusEOF) { - return; - } self.status = DLGPlayerStatusPlaying; [UIApplication sharedApplication].idleTimerDisabled = self.preventFromScreenLock; [self.player play]; @@ -207,28 +183,6 @@ - (void)pause { [self.btnPlay setTitle:@"|>" forState:UIControlStateNormal]; } -- (BOOL)doNextOperation { - if (self.nextOperation == DLGPlayerOperationNone) return NO; - switch (self.nextOperation) { - case DLGPlayerOperationOpen: - [self open]; - break; - case DLGPlayerOperationPlay: - [self play]; - break; - case DLGPlayerOperationPause: - [self pause]; - break; - case DLGPlayerOperationClose: - [self close]; - break; - default: - break; - } - self.nextOperation = DLGPlayerOperationNone; - return YES; -} - #pragma mark - Notifications - (void)notifyAppDidEnterBackground:(NSNotification *)notif { if (self.player.playing) { @@ -254,7 +208,6 @@ - (void)notifyPlayerClosed:(NSNotification *)notif { self.status = DLGPlayerStatusClosed; [self.aivBuffering stopAnimating]; [self destroyTimer]; - [self doNextOperation]; } - (void)notifyPlayerOpened:(NSNotification *)notif { @@ -292,9 +245,7 @@ - (void)notifyPlayerOpened:(NSNotification *)notif { [strongSelf showHUD]; }); - if (![self doNextOperation]) { - if (self.autoplay) [self play]; - } + if (self.autoplay) [self play]; } - (void)notifyPlayerBufferStateChanged:(NSNotification *)notif { @@ -323,12 +274,15 @@ - (void)notifyPlayerError:(NSNotification *)notif { [strongSelf.aivBuffering stopAnimating]; strongSelf.status = DLGPlayerStatusNone; - strongSelf.nextOperation = DLGPlayerOperationNone; }); - NSLog(@"Player decoder error: %@", error); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"Player decoder error: %@", error); + } } else if ([error.domain isEqualToString:DLGPlayerErrorDomainAudioManager]) { - NSLog(@"Player audio error: %@", error); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"Player audio error: %@", error); + } // I am not sure what will cause the audio error, // if it happens, please issue to me } @@ -527,7 +481,8 @@ - (void)showHUD { strongSelf.vBottomBar.alpha = 1.0f; } completion:^(BOOL finished) { - animatingHUD = NO; + __strong typeof(weakSelf)strongSelf = weakSelf; + strongSelf->animatingHUD = NO; }]; [self startTimerForHideHUD]; } @@ -549,7 +504,7 @@ - (void)hideHUD { strongSelf.vTopBar.hidden = YES; strongSelf.vBottomBar.hidden = YES; - animatingHUD = NO; + strongSelf->animatingHUD = NO; }]; [self stopTimerForHideHUD]; } diff --git a/DLGPlayer/DLGSimplePlayerViewController.h b/DLGPlayer/DLGSimplePlayerViewController.h new file mode 100644 index 0000000..5dac6bc --- /dev/null +++ b/DLGPlayer/DLGSimplePlayerViewController.h @@ -0,0 +1,46 @@ +// +// DLGSimplePlayerViewController.h +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#import +#import "DLGPlayer.h" + +@class DLGPlayer; +@class DLGSimplePlayerViewController; + +@protocol DLGSimplePlayerViewControllerDelegate +- (void)didBeginRenderInViewController:(DLGSimplePlayerViewController * _Nonnull)viewController; +- (void)viewController:(DLGSimplePlayerViewController * _Nonnull)viewController didChangeStatus:(DLGPlayerStatus)status; +- (void)viewController:(DLGSimplePlayerViewController * _Nonnull)viewController didReceiveError:(NSError * _Nonnull)error; +@end + +@interface DLGSimplePlayerViewController : UIViewController +@property (nonatomic, readonly) BOOL hasUrl; +@property (nonatomic) BOOL isAllowsFrameDrop; +@property (nonatomic) BOOL isAutoplay; +@property (nonatomic) BOOL isKeepLastFrame; +@property (nonatomic) BOOL isMute; +@property (nonatomic, readonly) BOOL isPlaying; +@property (nonatomic) BOOL isRepeat; +@property (nonatomic) BOOL preventFromScreenLock; +@property (nonatomic) BOOL restorePlayAfterAppEnterForeground; +@property (nonatomic) double frameDropDuration; +@property (nonatomic) double minBufferDuration; +@property (nonatomic) double maxBufferDuration; +@property (nonatomic) double speed; +@property (nullable, atomic, copy) NSString *url; +@property (nonnull, nonatomic, readonly) DLGPlayerControlStatus *controlStatus; +@property (nonatomic, readonly) DLGPlayerStatus status; +@property (nonnull, nonatomic, readonly) DLGPlayer *player; +@property (nullable, nonatomic, weak) id delegate; +- (void)clearView; +- (void)open; +- (void)play; +- (void)pause; +- (void)stop; +- (void)stopCompletely; +@end diff --git a/DLGPlayer/DLGSimplePlayerViewController.m b/DLGPlayer/DLGSimplePlayerViewController.m new file mode 100644 index 0000000..76a2272 --- /dev/null +++ b/DLGPlayer/DLGSimplePlayerViewController.m @@ -0,0 +1,328 @@ +// +// DLGSimplePlayerViewController.m +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#import "DLGSimplePlayerViewController.h" +#import "DLGPlayerUtils.h" + +typedef enum : NSUInteger { + DLGPlayerOperationNone, + DLGPlayerOperationOpen, + DLGPlayerOperationPlay, + DLGPlayerOperationPause, + DLGPlayerOperationClose, +} DLGPlayerOperation; + +@interface DLGSimplePlayerViewController () { + BOOL restorePlay; +} + +@property (nonatomic, strong) DLGPlayer *player; +@property (nonatomic, readwrite) DLGPlayerStatus status; +@end + +@implementation DLGSimplePlayerViewController + +- (void)dealloc { + NSLog(@"DLGSimplePlayerViewController dealloc"); +} + +#pragma mark - Constructors +- (instancetype)init { + self = [super init]; + if (self) { + [self initAll]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self initAll]; + } + return self; +} + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + [self initAll]; + } + return self; +} + +#pragma mark - View controller life cycle +- (void)viewDidLoad { + [super viewDidLoad]; + + [self addPlayerView]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + [self registerNotification]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + + [self unregisterNotification]; +} + +#pragma mark - getter/setter +- (BOOL)hasUrl { + NSString *trimmed = [_url stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; + return _url != nil && trimmed.length > 0; +} + +- (BOOL)isKeepLastFrame { + return _player.keepLastFrame; +} +- (void)setIsKeepLastFrame:(BOOL)isKeepLastFrame { + _player.keepLastFrame = isKeepLastFrame; +} + +- (BOOL)isMute { + return _player.mute; +} +- (void)setIsMute:(BOOL)isMute { + _player.mute = isMute; +} + +- (BOOL)isPlaying { + return _player.playing; +} + +- (double)frameDropDuration { + return _player.frameDropDuration; +} +- (void)setFrameDropDuration:(double)frameDropDuration { + _player.frameDropDuration = frameDropDuration; +} + +- (double)minBufferDuration { + return _player.minBufferDuration; +} +- (void)setMinBufferDuration:(double)minBufferDuration { + _player.minBufferDuration = minBufferDuration; +} + +- (double)maxBufferDuration { + return _player.maxBufferDuration; +} +- (void)setMaxBufferDuration:(double)maxBufferDuration { + _player.maxBufferDuration = maxBufferDuration; +} + +- (BOOL)isAllowsFrameDrop { + return _player.allowsFrameDrop; +} +- (void)setIsAllowsFrameDrop:(BOOL)isAllowsFrameDrop { + _player.allowsFrameDrop = isAllowsFrameDrop; +} + +- (double)speed { + return _player.speed; +} +- (void)setSpeed:(double)speed { + _player.speed = speed; +} + +- (void)setStatus:(DLGPlayerStatus)status { + _status = status; + [_controlStatus setStatus:_status]; + + __weak typeof(self)weakSelf = self; + + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (strongSelf && [strongSelf.delegate respondsToSelector:@selector(viewController:didChangeStatus:)]) { + [strongSelf.delegate viewController:strongSelf didChangeStatus:status]; + } + }); +} + +#pragma mark - Init +- (void)initAll { + _player = [[DLGPlayer alloc] init]; + _status = DLGPlayerStatusNone; + _controlStatus = [[DLGPlayerControlStatus alloc] initWithStatus:_status]; +} + +- (void)clearView { + [_player clearView]; +} + +- (void)open { + self.status = DLGPlayerStatusOpening; + [_player open:_url]; +} + +- (void)play { + [UIApplication sharedApplication].idleTimerDisabled = self.preventFromScreenLock; + [_player play]; + self.status = DLGPlayerStatusPlaying; +} + +- (void)replay { + _player.position = 0; + [self play]; +} + +- (void)pause { + [UIApplication sharedApplication].idleTimerDisabled = NO; + [_player pause]; + self.status = DLGPlayerStatusPaused; +} + +- (void)stop { + self.status = DLGPlayerStatusClosing; + [UIApplication sharedApplication].idleTimerDisabled = NO; + [_player close]; +} + +- (void)stopCompletely { + self.status = DLGPlayerStatusClosing; + [UIApplication sharedApplication].idleTimerDisabled = NO; + [_player closeCompletely]; +} + +#pragma mark - UI +- (void)addPlayerView { + UIView *v = _player.playerView; + v.translatesAutoresizingMaskIntoConstraints = NO; + + [self.view addSubview:v]; + + NSDictionary *views = NSDictionaryOfVariableBindings(v); + NSArray *ch = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[v]|" + options:0 + metrics:nil + views:views]; + [self.view addConstraints:ch]; + NSArray *cv = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[v]|" + options:0 + metrics:nil + views:views]; + [self.view addConstraints:cv]; +} + +#pragma mark - Notifications +- (void)registerNotification { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(notifyAppDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification object:nil]; + [nc addObserver:self selector:@selector(notifyAppWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification object:nil]; + [nc addObserver:self selector:@selector(notifyPlayerAudioOpened:) name:DLGPlayerNotificationAudioOpened object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerAudioClosed:) name:DLGPlayerNotificationAudioClosed object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerOpened:) name:DLGPlayerNotificationOpened object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerClosed:) name:DLGPlayerNotificationClosed object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerEOF:) name:DLGPlayerNotificationEOF object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerBufferStateChanged:) name:DLGPlayerNotificationBufferStateChanged object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerRenderBegan:) name:DLGPlayerNotificationRenderBegan object:_player]; + [nc addObserver:self selector:@selector(notifyPlayerError:) name:DLGPlayerNotificationError object:_player]; +} + +- (void)unregisterNotification { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)notifyAppDidEnterBackground:(NSNotification *)notif { + if (_player.playing) { + [self pause]; + if (_restorePlayAfterAppEnterForeground) restorePlay = YES; + } +} + +- (void)notifyAppWillEnterForeground:(NSNotification *)notif { + if (restorePlay) { + restorePlay = NO; + [self play]; + } +} + +- (void)notifyPlayerEOF:(NSNotification *)notif { + self.status = DLGPlayerStatusEOF; + + if (_isRepeat) { + [self replay]; + } else { + [self stop]; + } +} + +- (void)notifyPlayerClosed:(NSNotification *)notif { + self.status = DLGPlayerStatusClosed; +} + +- (void)notifyPlayerOpened:(NSNotification *)notif { + self.status = DLGPlayerStatusOpened; + + if (_isAutoplay) [self play]; +} + +- (void)notifyPlayerAudioOpened:(NSNotification *)notif { + self.status = DLGPlayerStatusAudioOpened; +} + +- (void)notifyPlayerAudioClosed:(NSNotification *)notif { + self.status = DLGPlayerStatusAudioClosed; +} + +- (void)notifyPlayerBufferStateChanged:(NSNotification *)notif { + NSDictionary *userInfo = notif.userInfo; + BOOL state = [userInfo[DLGPlayerNotificationBufferStateKey] boolValue]; + if (state) { + self.status = DLGPlayerStatusBuffering; + } else { + self.status = DLGPlayerStatusPlaying; + } +} + +- (void)notifyPlayerRenderBegan:(NSNotification *)notif { + if ([_delegate respondsToSelector:@selector(didBeginRenderInViewController:)]) { + [_delegate didBeginRenderInViewController:self]; + } +} + +- (void)notifyPlayerError:(NSNotification *)notif { + NSDictionary *userInfo = notif.userInfo; + NSError *error = userInfo[DLGPlayerNotificationErrorKey]; + + if ([error.domain isEqualToString:DLGPlayerErrorDomainDecoder]) { + self.status = DLGPlayerStatusNone; + + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"Player decoder error: %@", error); + } + } else if ([error.domain isEqualToString:DLGPlayerErrorDomainAudioManager]) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"Player audio error: %@", error); + } + } + + __weak typeof(self)weakSelf = self; + + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf)strongSelf = weakSelf; + + if (!strongSelf) { + return; + } + + if ([strongSelf.delegate respondsToSelector:@selector(viewController:didReceiveError:)]) { + [strongSelf.delegate viewController:strongSelf didReceiveError:error]; + [[NSNotificationCenter defaultCenter] postNotificationName:DLGPlayerNotificationError object:strongSelf userInfo:notif.userInfo]; + } + }); +} + +@end diff --git a/DLGPlayer/Info.plist b/DLGPlayer/Info.plist new file mode 100644 index 0000000..e1fe4cf --- /dev/null +++ b/DLGPlayer/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/DLGPlayer/codec/DLGPlayerAudioManager.h b/DLGPlayer/codec/DLGPlayerAudioManager.h index 7430f16..0913123 100644 --- a/DLGPlayer/codec/DLGPlayerAudioManager.h +++ b/DLGPlayer/codec/DLGPlayerAudioManager.h @@ -12,9 +12,13 @@ typedef void(^DLGPlayerAudioManagerFrameReaderBlock)(float *data, UInt32 num, UI @interface DLGPlayerAudioManager : NSObject -@property (nonatomic, copy) DLGPlayerAudioManagerFrameReaderBlock frameReaderBlock; +@property (nonatomic) BOOL mute; +@property (nonatomic) BOOL opened; @property (nonatomic) float volume; +@property (nonatomic, copy) DLGPlayerAudioManagerFrameReaderBlock frameReaderBlock; +@property (nonatomic) NSTimeInterval bufferDuration; +- (BOOL)open; - (BOOL)open:(NSError **)error; - (BOOL)play; - (BOOL)play:(NSError **)error; diff --git a/DLGPlayer/codec/DLGPlayerAudioManager.m b/DLGPlayer/codec/DLGPlayerAudioManager.m index 99ed490..6f1e9a8 100644 --- a/DLGPlayer/codec/DLGPlayerAudioManager.m +++ b/DLGPlayer/codec/DLGPlayerAudioManager.m @@ -12,32 +12,31 @@ #import #import #import +#import #define MAX_FRAME_SIZE 4096 #define MAX_CHANNEL 2 #define PREFERRED_SAMPLE_RATE 44100 #define PREFERRED_BUFFER_DURATION 0.023 -static OSStatus audioUnitRenderCallback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); +OSStatus audioUnitRenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); @interface DLGPlayerAudioManager () { BOOL _registeredKVO; - BOOL _opened; BOOL _closing; BOOL _shouldPlayAfterInterruption; BOOL _playing; double _sampleRate; + float *_audioData; UInt32 _bitsPerChannel; UInt32 _channelsPerFrame; AudioUnit _audioUnit; - float *_audioData; } - @end @implementation DLGPlayerAudioManager @@ -51,34 +50,51 @@ - (id)init { } - (void)initVars { + _closing = NO; + _mute = NO; _registeredKVO = NO; _opened = NO; - _closing = NO; _shouldPlayAfterInterruption = NO; _playing = NO; - _sampleRate = 0; _bitsPerChannel = 0; + _bufferDuration = 1; _channelsPerFrame = 0; + _sampleRate = 0; _audioUnit = NULL; _audioData = (float *)calloc(MAX_FRAME_SIZE * MAX_CHANNEL, sizeof(float)); _frameReaderBlock = nil; } - (void)dealloc { - NSLog(@"DLGPlayerAudioManager dealloc"); - if (_audioData != NULL) { - free(_audioData); - _audioData = NULL; + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"DLGPlayerAudioManager dealloc"); } + + [self unregisterNotifications]; + [self close]; + + free(_audioData); + _audioData = NULL; +} + +- (BOOL)open { + return [self open:nil]; } /* * https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/ConstructingAudioUnitApps/ConstructingAudioUnitApps.html */ - (BOOL)open:(NSError **)error { - AVAudioSession *session = [AVAudioSession sharedInstance]; + if (self.mute) { + return NO; + } + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> opening", self.hash); + } NSError *rawError = nil; + AVAudioSession *session = [AVAudioSession sharedInstance]; + if (![session setCategory:AVAudioSessionCategoryPlayback error:&rawError]) { [DLGPlayerUtils createError:error withDomain:DLGPlayerErrorDomainAudioManager @@ -87,17 +103,20 @@ - (BOOL)open:(NSError **)error { andRawError:rawError]; return NO; } - - NSTimeInterval prefferedIOBufferDuration = PREFERRED_BUFFER_DURATION; - if (![session setPreferredIOBufferDuration:prefferedIOBufferDuration error:&rawError]) { - NSLog(@"setPreferredIOBufferDuration: %.4f, error: %@", prefferedIOBufferDuration, rawError); + + if (![session setPreferredIOBufferDuration:_bufferDuration error:&rawError]) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"setPreferredIOBufferDuration: %.4f, error: %@", _bufferDuration, rawError); + } } - + double prefferedSampleRate = PREFERRED_SAMPLE_RATE; if (![session setPreferredSampleRate:prefferedSampleRate error:&rawError]) { - NSLog(@"setPreferredSampleRate: %.4f, error: %@", prefferedSampleRate, rawError); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"setPreferredSampleRate: %.4f, error: %@", prefferedSampleRate, rawError); + } } - + if (![session setActive:YES error:&rawError]) { [DLGPlayerUtils createError:error withDomain:DLGPlayerErrorDomainAudioManager @@ -106,16 +125,16 @@ - (BOOL)open:(NSError **)error { andRawError:rawError]; return NO; } - - AVAudioSessionRouteDescription *route = session.currentRoute; - if (route.outputs.count == 0) { + + AVAudioSessionRouteDescription *currentRoute = session.currentRoute; + if (currentRoute.outputs.count == 0) { [DLGPlayerUtils createError:error withDomain:DLGPlayerErrorDomainAudioManager andCode:DLGPlayerErrorCodeNoAudioOuput andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_NO_AUDIO_OUTPUT"]]; return NO; } - + NSInteger channels = session.outputNumberOfChannels; if (channels <= 0) { [DLGPlayerUtils createError:error @@ -124,7 +143,7 @@ - (BOOL)open:(NSError **)error { andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_NO_AUDIO_CHANNEL"]]; return NO; } - + double sampleRate = session.sampleRate; if (sampleRate <= 0) { [DLGPlayerUtils createError:error @@ -133,7 +152,7 @@ - (BOOL)open:(NSError **)error { andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_NO_AUDIO_SAMPLE_RATE"]]; return NO; } - + float volume = session.outputVolume; if (volume < 0) { [DLGPlayerUtils createError:error @@ -142,16 +161,21 @@ - (BOOL)open:(NSError **)error { andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_NO_AUDIO_VOLUME"]]; return NO; } - + if (![self initAudioUnitWithSampleRate:sampleRate andRenderCallback:audioUnitRenderCallback error:error]) { return NO; } - + [self registerNotifications]; + _sampleRate = sampleRate; _volume = volume; _opened = YES; + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> opened", self.hash); + } + return YES; } @@ -175,11 +199,10 @@ - (BOOL)initAudioUnitWithSampleRate:(double)sampleRate andRenderCallback:(AURend andRawError:rawError]; return NO; } - + AudioStreamBasicDescription streamDescr = {0}; UInt32 size = sizeof(AudioStreamBasicDescription); - status = AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, - 0, &streamDescr, &size); + status = AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDescr, &size); if (status != noErr) { NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; [DLGPlayerUtils createError:error @@ -189,21 +212,20 @@ - (BOOL)initAudioUnitWithSampleRate:(double)sampleRate andRenderCallback:(AURend andRawError:rawError]; return NO; } - + streamDescr.mSampleRate = sampleRate; - status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, - 0, &streamDescr, size); - if (status != noErr) { + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDescr, size); + if (DLGPlayerUtils.debugEnabled && status != noErr) { NSLog(@"FAILED to set audio sample rate: %f, error: %d", sampleRate, (int)status); } - + _bitsPerChannel = streamDescr.mBitsPerChannel; _channelsPerFrame = streamDescr.mChannelsPerFrame; - + AURenderCallbackStruct renderCallbackStruct; renderCallbackStruct.inputProc = renderCallback; renderCallbackStruct.inputProcRefCon = (__bridge void *)(self); - + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallbackStruct, sizeof(AURenderCallbackStruct)); if (status != noErr) { NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; @@ -214,7 +236,7 @@ - (BOOL)initAudioUnitWithSampleRate:(double)sampleRate andRenderCallback:(AURend andRawError:rawError]; return NO; } - + status = AudioUnitInitialize(audioUnit); if (status != noErr) { NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; @@ -236,66 +258,75 @@ - (BOOL)close { } - (BOOL)close:(NSArray **)errors { - if (_closing) return NO; - _closing = YES; + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> closing: %d", self.hash, _closing); + } + if (!_opened || _closing) { + return NO; + } NSMutableArray *errs = nil; if (errors != nil) errs = [NSMutableArray array]; + _closing = YES; + BOOL closed = YES; - if (_opened) { - [self pause]; - - [self unregisterNotifications]; - - OSStatus status = AudioUnitUninitialize(_audioUnit); - if (status != noErr) { - closed = NO; - if (errs != nil) { - NSError *error = nil; - NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; - [DLGPlayerUtils createError:&error - withDomain:DLGPlayerErrorDomainAudioManager - andCode:DLGPlayerErrorCodeCannotUninitAudioUnit - andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_UNINIT_AUDIO_UNIT"] - andRawError:rawError]; - [errs addObject:error]; - } + [self pause]; + [self unregisterNotifications]; + + OSStatus status = AudioUnitUninitialize(_audioUnit); + if (status != noErr) { + closed = NO; + if (errs != nil) { + NSError *error = nil; + NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + [DLGPlayerUtils createError:&error + withDomain:DLGPlayerErrorDomainAudioManager + andCode:DLGPlayerErrorCodeCannotUninitAudioUnit + andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_UNINIT_AUDIO_UNIT"] + andRawError:rawError]; + [errs addObject:error]; } - - status = AudioComponentInstanceDispose(_audioUnit); - if (status != noErr) { - closed = NO; - if (errs != nil) { - NSError *error = nil; - NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; - [DLGPlayerUtils createError:&error - withDomain:DLGPlayerErrorDomainAudioManager - andCode:DLGPlayerErrorCodeCannotDisposeAudioUnit - andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_DISPOSE_AUDIO_UNIT"] - andRawError:rawError]; - [errs addObject:error]; - } + } + + status = AudioComponentInstanceDispose(_audioUnit); + if (status != noErr) { + closed = NO; + if (errs != nil) { + NSError *error = nil; + NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + [DLGPlayerUtils createError:&error + withDomain:DLGPlayerErrorDomainAudioManager + andCode:DLGPlayerErrorCodeCannotDisposeAudioUnit + andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_DISPOSE_AUDIO_UNIT"] + andRawError:rawError]; + [errs addObject:error]; } - - NSError *error = nil; - AVAudioSession *session = [AVAudioSession sharedInstance]; - if (![session setActive:NO error:&error]) { - closed = NO; - if (errs != nil) { - NSError *error = nil; - NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; - [DLGPlayerUtils createError:&error - withDomain:DLGPlayerErrorDomainAudioManager - andCode:DLGPlayerErrorCodeCannotDeactivateAudio - andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_DEACTIVATE_AUDIO"] - andRawError:rawError]; - [errs addObject:error]; - } + } + + AVAudioSession *session = [AVAudioSession sharedInstance]; + NSError *error = nil; + + if (![session setActive:NO error:&error]) { + if (errs != nil) { + NSError *error = nil; + NSError *rawError = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + [DLGPlayerUtils createError:&error + withDomain:DLGPlayerErrorDomainAudioManager + andCode:DLGPlayerErrorCodeCannotDeactivateAudio + andMessage:[DLGPlayerUtils localizedString:@"DLG_PLAYER_STRINGS_CANNOT_DEACTIVATE_AUDIO"] + andRawError:rawError]; + [errs addObject:error]; } + } + + if (closed) { + [self clear]; - if (closed) _opened = NO; + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> closed", self.hash); + } } _closing = NO; @@ -308,7 +339,14 @@ - (BOOL)play { } - (BOOL)play:(NSError **)error { + if (self.mute) { + return _playing; + } + if (_opened) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> play", self.hash); + } OSStatus status = AudioOutputUnitStart(_audioUnit); _playing = (status == noErr); if (!_playing) { @@ -328,7 +366,14 @@ - (BOOL)pause { } - (BOOL)pause:(NSError **)error { + if (self.mute) { + return _playing; + } + if (_playing) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"[Audio] %zd -> pause", self.hash); + } OSStatus status = AudioOutputUnitStop(_audioUnit); _playing = !(status == noErr); if (_playing) { @@ -343,6 +388,13 @@ - (BOOL)pause:(NSError **)error { return !_playing; } +- (void)clear { + _opened = NO; + _closing = NO; + _playing = NO; + _audioUnit = NULL; +} + - (OSStatus)render:(AudioBufferList *)ioData count:(UInt32)inNumberFrames { UInt32 num = ioData->mNumberBuffers; for (UInt32 i = 0; i < num; ++i) { @@ -375,6 +427,7 @@ - (OSStatus)render:(AudioBufferList *)ioData count:(UInt32)inNumberFrames { } } + return noErr; } @@ -399,11 +452,11 @@ - (void)registerNotifications { object:nil]; if (!_registeredKVO) { - AVAudioSession *session = [AVAudioSession sharedInstance]; - [session addObserver:self - forKeyPath:@"outputVolume" - options:0 - context:nil]; + AVAudioSession *session = [AVAudioSession sharedInstance]; + [session addObserver:self + forKeyPath:@"outputVolume" + options:0 + context:nil]; _registeredKVO = YES; } } @@ -414,8 +467,8 @@ - (void)unregisterNotifications { if (_registeredKVO) { AVAudioSession *session = [AVAudioSession sharedInstance]; [session removeObserver:self forKeyPath:@"outputVolume"]; - _registeredKVO = NO; } + _registeredKVO = NO; } - (void)notifyAudioSessionRouteChanged:(NSNotification *)notif { @@ -448,12 +501,12 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N @end -static OSStatus audioUnitRenderCallback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData) { +OSStatus audioUnitRenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) { DLGPlayerAudioManager *manager = (__bridge DLGPlayerAudioManager *)(inRefCon); return [manager render:ioData count:inNumberFrames]; } diff --git a/DLGPlayer/codec/DLGPlayerDecoder.h b/DLGPlayer/codec/DLGPlayerDecoder.h index ad4aecc..c498fe3 100644 --- a/DLGPlayer/codec/DLGPlayerDecoder.h +++ b/DLGPlayer/codec/DLGPlayerDecoder.h @@ -10,14 +10,16 @@ @interface DLGPlayerDecoder : NSObject -@property (nonatomic) BOOL isYUV; -@property (nonatomic) BOOL hasVideo; @property (nonatomic) BOOL hasAudio; +@property (nonatomic) BOOL hasVideo; @property (nonatomic) BOOL hasPicture; @property (nonatomic) BOOL isEOF; +@property (nonatomic) BOOL isYUV; +@property (nonatomic) BOOL mute; @property (nonatomic) double rotation; @property (nonatomic) double duration; +@property (nonatomic) double speed; @property (nonatomic, strong) NSDictionary *metadata; @property (nonatomic) UInt32 audioChannels; @@ -27,13 +29,12 @@ @property (nonatomic) double videoTimebase; @property (nonatomic) double audioTimebase; -- (BOOL)open:(NSString *)url error:(NSError **)error; - (void)close; - (void)prepareClose; - (NSArray *)readFrames; +- (BOOL)open:(NSString *)url error:(NSError **)error; - (void)seek:(double)position; - (int)videoWidth; - (int)videoHeight; -- (BOOL)isYUV; @end diff --git a/DLGPlayer/codec/DLGPlayerDecoder.m b/DLGPlayer/codec/DLGPlayerDecoder.m index e67c9cc..0be2c2b 100644 --- a/DLGPlayer/codec/DLGPlayerDecoder.m +++ b/DLGPlayer/codec/DLGPlayerDecoder.m @@ -23,10 +23,10 @@ #define DLGPlayerIOTimeout 30 -static NSTimeInterval g_dIOStartTime = 0; -static bool g_bPrepareClose = FALSE; +NSTimeInterval g_dIOStartTime = 0; +bool g_bPrepareClose = FALSE; -static int interruptCallback(void *context) { +int interruptCallback(void *context) { NSTimeInterval t = [NSDate timeIntervalSinceReferenceDate]; NSTimeInterval dt = t - g_dIOStartTime; if (g_bPrepareClose || dt > DLGPlayerIOTimeout) return 1; @@ -43,6 +43,7 @@ @interface DLGPlayerDecoder () { AVCodecContext *m_pVideoCodecContext; struct SwsContext *m_pVideoSwsContext; int m_nPictureStream; + int64_t ptsPrevVideo; // Audio int m_nAudioStream; @@ -51,14 +52,27 @@ @interface DLGPlayerDecoder () { SwrContext *m_pAudioSwrContext; void *m_pAudioSwrBuffer; int m_nAudioSwrBufferSize; + int64_t ptsPrevAudio; } @end @implementation DLGPlayerDecoder +- (void)setMute:(BOOL)mute { + if (mute == _mute) + return; + + _mute = mute; + + [self setAudioSwrContext]; +} + - (void)dealloc { - NSLog(@"DLGPlayerDecoder dealloc"); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"DLGPlayerDecoder dealloc"); + } + free(m_pAudioSwrBuffer); } - (BOOL)open:(NSString *)url error:(NSError **)error { @@ -76,6 +90,7 @@ - (BOOL)open:(NSString *)url error:(NSError **)error { // 2. Open Input AVFormatContext *fmtctx = NULL; int ret = avformat_open_input(&fmtctx, [url UTF8String], NULL, NULL); + if (ret != 0) { if (fmtctx != NULL) avformat_free_context(fmtctx); [DLGPlayerUtils createError:error @@ -142,32 +157,34 @@ - (BOOL)open:(NSString *)url error:(NSError **)error { if (astream >= 0 && acodectx != NULL) { aframe = av_frame_alloc(); [DLGPlayerDecoder stream:fmtctx->streams[astream] fps:NULL timebase:&_audioTimebase default:0.025]; - if (aframe == NULL) { astream = -1; if (acodectx != NULL) avcodec_free_context(&acodectx); } - aswrctx = swr_alloc_set_opts(NULL, - av_get_default_channel_layout(_audioChannels), - AV_SAMPLE_FMT_S16, - _audioSampleRate, - av_get_default_channel_layout(acodectx->channels), - acodectx->sample_fmt, - acodectx->sample_rate, - 0, - NULL); - if (aswrctx == NULL) { - astream = -1; - if (acodectx != NULL) avcodec_free_context(&acodectx); - if (aframe != NULL) av_frame_free(&aframe); - } - - ret = swr_init(aswrctx); - if (ret < 0) { - astream = -1; - if (aswrctx != NULL) swr_free(&aswrctx); - if (acodectx != NULL) avcodec_free_context(&acodectx); - if (aframe != NULL) av_frame_free(&aframe); + + if (!_mute) { + aswrctx = swr_alloc_set_opts(NULL, + av_get_default_channel_layout(_audioChannels), + AV_SAMPLE_FMT_S16, + _audioSampleRate, + av_get_default_channel_layout(acodectx->channels), + acodectx->sample_fmt, + acodectx->sample_rate, + 0, + NULL); + if (aswrctx == NULL) { + astream = -1; + if (acodectx != NULL) avcodec_free_context(&acodectx); + if (aframe != NULL) av_frame_free(&aframe); + } else { + ret = swr_init(aswrctx); + if (ret < 0) { + astream = -1; + if (aswrctx != NULL) swr_free(&aswrctx); + if (acodectx != NULL) avcodec_free_context(&acodectx); + if (aframe != NULL) av_frame_free(&aframe); + } + } } } @@ -193,16 +210,20 @@ - (BOOL)open:(NSString *)url error:(NSError **)error { m_pAudioCodecContext = acodectx; m_pAudioSwrContext = aswrctx; - self.isYUV = isYUV; - self.hasVideo = vstream >= 0; - self.hasAudio = astream >= 0; - self.hasPicture = picstream >= 0; - self.isEOF = NO; + ptsPrevAudio = 0; + ptsPrevVideo = 0; - self.rotation = rotation; + _isYUV = isYUV; + _hasVideo = vstream >= 0; + _hasAudio = astream >= 0; + _hasPicture = picstream >= 0; + _isEOF = NO; + + _rotation = rotation; int64_t duration = fmtctx->duration; - self.duration = (duration == AV_NOPTS_VALUE ? -1 : ((double)duration / AV_TIME_BASE)); - self.metadata = [self findMetadata:fmtctx]; + _duration = (duration == AV_NOPTS_VALUE ? -1 : ((double)duration / AV_TIME_BASE)); + _duration /= _speed; + _metadata = [self findMetadata:fmtctx]; g_bPrepareClose = FALSE; AVIOInterruptCB icb = {interruptCallback, NULL}; @@ -211,6 +232,39 @@ - (BOOL)open:(NSString *)url error:(NSError **)error { return YES; } +- (void)setAudioSwrContext { + if (m_pAudioSwrContext || !m_pAudioCodecContext || _mute) + return; + + int ret; + SwrContext *aswrctx = NULL; + + aswrctx = swr_alloc_set_opts(NULL, + av_get_default_channel_layout(_audioChannels), + AV_SAMPLE_FMT_S16, + _audioSampleRate, + av_get_default_channel_layout(m_pAudioCodecContext->channels), + m_pAudioCodecContext->sample_fmt, + m_pAudioCodecContext->sample_rate, + 0, + NULL); + if (aswrctx == NULL) { + m_nAudioStream = -1; + if (m_pAudioCodecContext != NULL) avcodec_free_context(&m_pAudioCodecContext); + if (m_pAudioFrame != NULL) av_frame_free(&m_pAudioFrame); + } else { + ret = swr_init(aswrctx); + if (ret < 0) { + m_nAudioStream = -1; + if (aswrctx != NULL) swr_free(&m_pAudioSwrContext); + if (m_pAudioCodecContext != NULL) avcodec_free_context(&m_pAudioCodecContext); + if (m_pAudioFrame != NULL) av_frame_free(&m_pAudioFrame); + } + } + + m_pAudioSwrContext = aswrctx; +} + - (NSDictionary *)findMetadata:(AVFormatContext *)fmtctx { if (fmtctx == NULL || fmtctx->metadata == NULL) return nil; @@ -218,10 +272,12 @@ - (NSDictionary *)findMetadata:(AVFormatContext *)fmtctx { AVDictionary *metadata = fmtctx->metadata; AVDictionaryEntry *entry = av_dict_get(metadata, "", NULL, AV_DICT_IGNORE_SUFFIX); while (entry != NULL) { - NSString *key = [NSString stringWithCString:entry->key encoding:NSUTF8StringEncoding]; - NSString *value = [NSString stringWithCString:entry->value encoding:NSUTF8StringEncoding]; - if (key != nil && value != nil) md[key] = value; - entry = av_dict_get(metadata, "", entry, AV_DICT_IGNORE_SUFFIX); + @autoreleasepool { + NSString *key = [NSString stringWithCString:entry->key encoding:NSUTF8StringEncoding]; + NSString *value = [NSString stringWithCString:entry->value encoding:NSUTF8StringEncoding]; + if (key != nil && value != nil) md[key] = value; + entry = av_dict_get(metadata, "", entry, AV_DICT_IGNORE_SUFFIX); + } } return md; @@ -234,11 +290,13 @@ - (int)findVideoStream:(AVFormatContext *)fmtctx context:(AVCodecContext **)cont if (fmtctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { int disposition = fmtctx->streams[i]->disposition; if ((disposition & AV_DISPOSITION_ATTACHED_PIC) == 0) { // Not attached picture - AVCodecContext *codectx = [self openVideoCodec:fmtctx stream:i]; - if (codectx != NULL) { - if (context != NULL) *context = codectx; - stream = i; - break; + @autoreleasepool { + AVCodecContext *codectx = [self openVideoCodec:fmtctx stream:i]; + if (codectx != NULL) { + if (context != NULL) *context = codectx; + stream = i; + break; + } } } else { if (pictureStream != NULL) *pictureStream = i; @@ -276,11 +334,13 @@ - (int)findAudioStream:(AVFormatContext *)fmtctx context:(AVCodecContext **)cont int stream = -1; for (int i = 0; i < fmtctx->nb_streams; ++i) { if (fmtctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - AVCodecContext *codectx = [self openAudioCodec:fmtctx stream:i]; - if (codectx != NULL) { - if (context != NULL) *context = codectx; - stream = i; - break; + @autoreleasepool { + AVCodecContext *codectx = [self openAudioCodec:fmtctx stream:i]; + if (codectx != NULL) { + if (context != NULL) *context = codectx; + stream = i; + break; + } } } } @@ -323,15 +383,16 @@ - (void)close { [self closePictureStream]; if (m_pFormatContext != NULL) avformat_close_input(&m_pFormatContext); avformat_network_deinit(); - self.isYUV = NO; - self.hasVideo = NO; - self.hasAudio = NO; - self.hasPicture = NO; - self.isEOF = NO; + _isYUV = NO; + _hasVideo = NO; + _hasAudio = NO; + _hasPicture = NO; + _isEOF = NO; } - (void)closeVideoStream { m_nVideoStream = -1; + ptsPrevVideo = 0; if (m_pVideoFrame != NULL) av_frame_free(&m_pVideoFrame); if (m_pVideoCodecContext != NULL) avcodec_free_context(&m_pVideoCodecContext); if (m_pVideoSwsContext != NULL) { sws_freeContext(m_pVideoSwsContext); m_pVideoSwsContext = NULL; } @@ -339,6 +400,7 @@ - (void)closeVideoStream { - (void)closeAudioStream { m_nAudioStream = -1; + ptsPrevAudio = 0; if (m_pAudioFrame != NULL) av_frame_free(&m_pAudioFrame); if (m_pAudioCodecContext != NULL) avcodec_free_context(&m_pAudioCodecContext); if (m_pAudioSwrContext != NULL) swr_free(&m_pAudioSwrContext); @@ -358,7 +420,9 @@ - (void)flush:(AVCodecContext *)codectx frame:(AVFrame *)frame { #pragma mark - Handle Frames - (NSArray *)readFrames { - if ((m_nVideoStream < 0 && m_nAudioStream < 0) || _isEOF) return nil; + if ((m_nVideoStream < 0 && m_nAudioStream < 0) || _isEOF) { + return nil; + } AVFormatContext *fmtctx = m_pFormatContext; const int vstream = m_nVideoStream; @@ -378,31 +442,46 @@ - (NSArray *)readFrames { NSMutableArray *frames = [NSMutableArray arrayWithCapacity:15]; BOOL reading = YES; + while (reading) { g_dIOStartTime = [NSDate timeIntervalSinceReferenceDate]; int ret = av_read_frame(fmtctx, &packet); if (ret < 0) { - if (ret == AVERROR_EOF) self.isEOF = YES; - char *e = av_err2str(ret); - NSLog(@"read frame error: %s", e); + if (ret == AVERROR_EOF) _isEOF = YES; + if (DLGPlayerUtils.debugEnabled) { + char *e = av_err2str(ret); + NSLog(@"DLGPlayer read frame error: %s", e); + } break; } - /* - * https://ffmpeg.org/doxygen/3.1/group__lavc__encdec.html - */ - NSArray *fs = nil; - if (packet.stream_index == vstream) { - fs = [self handleVideoPacket:&packet byContext:vcodectx andFrame:vframe andSwsContext:swsctx andSwsFrame:vswsframe]; - reading = NO; - } else if (packet.stream_index == astream) { - fs = [self handleAudioPacket:&packet byContext:acodectx andFrame:aframe andSwrContext:swrctx andSwrBuffer:swrbuf andSwrBufferSize:swrbufsize]; - if (!_hasVideo) reading = NO; - } else if (packet.stream_index == picstream) { - fs = [self handlePicturePacket:&packet]; - if (!_hasVideo && !_hasAudio) reading = NO; + @autoreleasepool { + /* + * https://ffmpeg.org/doxygen/3.1/group__lavc__encdec.html + */ + NSArray *fs = nil; + + if (packet.stream_index == vstream) { + fs = [self handleVideoPacket:&packet byContext:vcodectx andFrame:vframe andSwsContext:swsctx andSwsFrame:vswsframe]; + reading = NO; + } else if (packet.stream_index == astream) { + fs = [self handleAudioPacket:&packet byContext:acodectx andFrame:aframe andSwrContext:swrctx andSwrBuffer:swrbuf andSwrBufferSize:swrbufsize]; + + if (!_hasVideo) { + reading = NO; + } + } else if (packet.stream_index == picstream) { + fs = [self handlePicturePacket:&packet]; + + if (!_hasVideo && !_hasAudio) { + reading = NO; + } + } + + if (fs != nil && fs.count > 0) { + [frames addObjectsFromArray:fs]; + } } - if (fs != nil && fs.count > 0) [frames addObjectsFromArray:fs]; av_packet_unref(&packet); } @@ -446,12 +525,15 @@ - (NSArray *)readFrames { CGDataProviderRelease(provider); NSMutableArray *frames = [NSMutableArray array]; - DLGPlayerVideoRGBFrame *frame = [[DLGPlayerVideoRGBFrame alloc] init]; - frame.data = [NSData dataWithBytes:imageData length:length]; - frame.width = (int)width; - frame.height = (int)height; - frame.hasAlpha = YES; - [frames addObject:frame]; + + @autoreleasepool { + DLGPlayerVideoRGBFrame *frame = [[DLGPlayerVideoRGBFrame alloc] init]; + frame.data = [NSData dataWithBytes:imageData length:length]; + frame.width = (int)width; + frame.height = (int)height; + frame.hasAlpha = YES; + [frames addObject:frame]; + } free(imageData); @@ -460,59 +542,77 @@ - (NSArray *)readFrames { - (NSArray *)handleVideoPacket:(AVPacket *)packet byContext:(AVCodecContext *)context andFrame:(AVFrame *)frame andSwsContext:(struct SwsContext *)swsctx andSwsFrame:(AVFrame *)swsframe { int ret = avcodec_send_packet(context, packet); - if (ret != 0) { NSLog(@"avcodec_send_packet: %d", ret); return nil; } + if (ret != 0) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"avcodec_send_packet: %d", ret); + } + return nil; + } NSMutableArray *frames = [NSMutableArray array]; do { - ret = avcodec_receive_frame(context, frame); - if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { break; } - else if (ret < 0) { NSLog(@"avcodec_receive_frame: %d", ret); break; } + ret = avcodec_receive_frame(context, frame); + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + break; + } else if (ret < 0) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"avcodec_receive_frame: %d", ret); + } + break; + } - DLGPlayerVideoFrame *f = nil; - const int width = context->width; - const int height = context->height; - if (_isYUV) { - DLGPlayerVideoYUVFrame *yuv = [[DLGPlayerVideoYUVFrame alloc] init]; - yuv.Y = [DLGPlayerDecoder dataFromVideoFrame:frame->data[0] - linesize:frame->linesize[0] - width:width - height:height]; - yuv.Cb = [DLGPlayerDecoder dataFromVideoFrame:frame->data[1] - linesize:frame->linesize[1] - width:width / 2 - height:height / 2]; - yuv.Cr = [DLGPlayerDecoder dataFromVideoFrame:frame->data[2] - linesize:frame->linesize[2] - width:width / 2 - height:height / 2]; - f = yuv; - } else { - sws_scale(swsctx, - (uint8_t const **)frame->data, - frame->linesize, - 0, - context->height, - swsframe->data, - swsframe->linesize); + @autoreleasepool { + DLGPlayerVideoFrame *f = nil; + const int width = context->width; + const int height = context->height; + if (_isYUV) { + DLGPlayerVideoYUVFrame *yuv = [[DLGPlayerVideoYUVFrame alloc] init]; + yuv.Y = [DLGPlayerDecoder dataFromVideoFrame:frame->data[0] + linesize:frame->linesize[0] + width:width + height:height]; + yuv.Cb = [DLGPlayerDecoder dataFromVideoFrame:frame->data[1] + linesize:frame->linesize[1] + width:width / 2 + height:height / 2]; + yuv.Cr = [DLGPlayerDecoder dataFromVideoFrame:frame->data[2] + linesize:frame->linesize[2] + width:width / 2 + height:height / 2]; + f = yuv; + } else { + sws_scale(swsctx, + (uint8_t const **)frame->data, + frame->linesize, + 0, + context->height, + swsframe->data, + swsframe->linesize); + + DLGPlayerVideoRGBFrame *rgb = [[DLGPlayerVideoRGBFrame alloc] init]; + rgb.linesize = swsframe->linesize[0]; + rgb.data = [NSData dataWithBytes:swsframe->data[0] length:rgb.linesize * height]; + f = rgb; + } - DLGPlayerVideoRGBFrame *rgb = [[DLGPlayerVideoRGBFrame alloc] init]; - rgb.linesize = swsframe->linesize[0]; - rgb.data = [NSData dataWithBytes:swsframe->data[0] length:rgb.linesize * height]; - f = rgb; - } - - f.width = width; - f.height = height; - f.position = frame->best_effort_timestamp * _videoTimebase; - double duration = frame->pkt_duration; - if (duration > 0) { - f.duration = duration * _videoTimebase; - f.duration += frame->repeat_pict * _videoTimebase * 0.5; - } else { - f.duration = 1 / _videoFPS; + f.width = width; + f.height = height; + f.position = frame->best_effort_timestamp * _videoTimebase; + + double duration = frame->pts - ptsPrevVideo; + + if (duration > 0) { + f.duration = duration * _videoTimebase / _speed; + f.duration += (frame->repeat_pict * _videoTimebase * 0.5) / _speed; + } else { + f.duration = 1 / _videoFPS / _speed; + } + + [frames addObject:f]; + + ptsPrevVideo = frame->pts; } - [frames addObject:f]; } while(ret == 0); return frames; @@ -520,13 +620,24 @@ - (NSArray *)readFrames { - (NSArray *)handleAudioPacket:(AVPacket *)packet byContext:(AVCodecContext *)context andFrame:(AVFrame *)frame andSwrContext:(SwrContext *)swrctx andSwrBuffer:(void **)swrbuf andSwrBufferSize:(int *)swrbufsize { int ret = avcodec_send_packet(context, packet); - if (ret != 0) { NSLog(@"avcodec_send_packet: %d", ret); return nil; } + if (ret != 0) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"avcodec_send_packet: %d", ret); + } + return nil; + } NSMutableArray *frames = [NSMutableArray array]; do { ret = avcodec_receive_frame(context, frame); - if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { break; } - else if (ret < 0) { NSLog(@"avcodec_receive_frame: %d", ret); break; } + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + break; + } else if (ret < 0) { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"avcodec_receive_frame: %d", ret); + } + break; + } if (frame->data[0] == NULL) continue; const float sampleRate = _audioSampleRate; @@ -544,6 +655,7 @@ - (NSArray *)readFrames { samples, AV_SAMPLE_FMT_S16, 1); + if (*swrbuf == NULL || *swrbufsize < bufsize) { *swrbufsize = bufsize; *swrbuf = realloc(*swrbuf, bufsize); @@ -552,14 +664,18 @@ - (NSArray *)readFrames { Byte *o[2] = { *swrbuf, 0 }; samplesPerChannel = swr_convert(swrctx, o, samples, (const uint8_t **)frame->data, frame->nb_samples); if (samplesPerChannel < 0) { - NSLog(@"failed to resample audio"); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"failed to resample audio"); + } return nil; } data = *swrbuf; } else { if (context->sample_fmt != AV_SAMPLE_FMT_S16) { - NSLog(@"invalid audio format"); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"invalid audio format"); + } return nil; } @@ -570,7 +686,7 @@ - (NSArray *)readFrames { NSUInteger elements = samplesPerChannel * channels; NSUInteger dataLength = elements * sizeof(float); NSMutableData *mdata = [NSMutableData dataWithLength:dataLength]; - + float scalar = 1.0f / INT16_MAX; vDSP_vflt16(data, 1, mdata.mutableBytes, 1, elements); vDSP_vsmul(mdata.mutableBytes, 1, &scalar, mdata.mutableBytes, 1, elements); @@ -578,10 +694,16 @@ - (NSArray *)readFrames { DLGPlayerAudioFrame *f = [[DLGPlayerAudioFrame alloc] init]; f.data = mdata; f.position = frame->best_effort_timestamp * _audioTimebase; - f.duration = frame->pkt_duration * _audioTimebase; - if (f.duration == 0) - f.duration = f.data.length / (sizeof(float) * channels * sampleRate); + double duration = frame->pts - ptsPrevAudio; + + if (duration > 0) { + f.duration = duration * _audioTimebase / _speed; + } else { + f.duration = f.data.length / (sizeof(float) * channels * sampleRate) / _speed; + } + + ptsPrevAudio = frame->pts; [frames addObject:f]; } while(ret == 0); @@ -592,6 +714,7 @@ - (NSArray *)readFrames { #pragma mark - Seek - (void)seek:(double)position { _isEOF = NO; + if (_hasVideo) { NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; int64_t ts = (int64_t)(position / _videoTimebase); @@ -599,7 +722,9 @@ - (void)seek:(double)position { avcodec_flush_buffers(m_pVideoCodecContext); NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate]; NSTimeInterval dt = end - start; - NSLog(@"seek video: %.4f, start: %.4f, end: %.4f", dt, start, end); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"seek video: %.4f, start: %.4f, end: %.4f", dt, start, end); + } } else if (_hasAudio) { NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; int64_t ts = (int64_t)(position / _audioTimebase); @@ -607,7 +732,9 @@ - (void)seek:(double)position { avcodec_flush_buffers(m_pAudioCodecContext); NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate]; NSTimeInterval dt = end - start; - NSLog(@"seek audio: %.4f, start: %.4f, end: %.4f", dt, start, end); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"seek audio: %.4f, start: %.4f, end: %.4f", dt, start, end); + } } } diff --git a/DLGPlayer/common/DLGPlayerDef.h b/DLGPlayer/common/DLGPlayerDef.h index d67ed97..7e91686 100644 --- a/DLGPlayer/common/DLGPlayerDef.h +++ b/DLGPlayer/common/DLGPlayerDef.h @@ -11,7 +11,8 @@ #define DLGPlayerLocalizedStringTable @"DLGPlayerStrings" -#define DLGPlayerMinBufferDuration 2 +#define DLGPlayerFrameDropDuration 4 +#define DLGPlayerMinBufferDuration 1 #define DLGPlayerMaxBufferDuration 5 #define DLGPlayerErrorDomainDecoder @"DLGPlayerDecoder" @@ -39,10 +40,13 @@ #define DLGPlayerErrorCodeCannotStopAudioUnit -19 #pragma mark - Notification +#define DLGPlayerNotificationAudioOpened @"DLGPlayerNotificationAudioOpened" +#define DLGPlayerNotificationAudioClosed @"DLGPlayerNotificationAudioClosed" #define DLGPlayerNotificationOpened @"DLGPlayerNotificationOpened" #define DLGPlayerNotificationClosed @"DLGPlayerNotificationClosed" #define DLGPlayerNotificationEOF @"DLGPlayerNotificationEOF" #define DLGPlayerNotificationBufferStateChanged @"DLGPlayerNotificationBufferStateChanged" +#define DLGPlayerNotificationRenderBegan @"DLGPlayerNotificationRenderBegan" #define DLGPlayerNotificationError @"DLGPlayerNotificationError" #pragma mark - Notification Key @@ -51,4 +55,18 @@ #define DLGPlayerNotificationErrorKey @"DLGPlayerNotificationErrorKey" #define DLGPlayerNotificationRawErrorKey @"DLGPlayerNotificationRawErrorKey" +typedef NS_ENUM(NSUInteger, DLGPlayerStatus) { + DLGPlayerStatusNone, + DLGPlayerStatusOpening, + DLGPlayerStatusAudioOpened, + DLGPlayerStatusOpened, + DLGPlayerStatusPlaying, + DLGPlayerStatusBuffering, + DLGPlayerStatusPaused, + DLGPlayerStatusEOF, + DLGPlayerStatusClosing, + DLGPlayerStatusAudioClosed, + DLGPlayerStatusClosed +}; + #endif /* DLGPlayerDef_h */ diff --git a/DLGPlayer/common/DLGPlayerUtils.h b/DLGPlayer/common/DLGPlayerUtils.h index 1a03bc6..2982e8f 100644 --- a/DLGPlayer/common/DLGPlayerUtils.h +++ b/DLGPlayer/common/DLGPlayerUtils.h @@ -12,7 +12,10 @@ + (BOOL)createError:(NSError **)error withDomain:(NSString *)domain andCode:(NSInteger)code andMessage:(NSString *)message; + (BOOL)createError:(NSError **)error withDomain:(NSString *)domain andCode:(NSInteger)code andMessage:(NSString *)message andRawError:(NSError *)rawError; ++ (BOOL)debugEnabled; ++ (BOOL)isMetalSupport; + (NSString *)localizedString:(NSString *)name; + (NSString *)durationStringFromSeconds:(int)seconds; ++ (void)setDebugEnabled:(BOOL)enabled; @end diff --git a/DLGPlayer/common/DLGPlayerUtils.m b/DLGPlayer/common/DLGPlayerUtils.m index eba9925..3f54128 100644 --- a/DLGPlayer/common/DLGPlayerUtils.m +++ b/DLGPlayer/common/DLGPlayerUtils.m @@ -8,6 +8,11 @@ #import "DLGPlayerUtils.h" #import "DLGPlayerDef.h" +@import MetalKit; + +static BOOL debugEnabled = NO; +static BOOL isMetalSupport = NO; +static BOOL isMetalSupportChecked = NO; @implementation DLGPlayerUtils @@ -32,8 +37,12 @@ + (BOOL)createError:(NSError **)error withDomain:(NSString *)domain andCode:(NSI return YES; } ++ (BOOL)debugEnabled { + return debugEnabled; +} + + (NSString *)localizedString:(NSString *)name { - return NSLocalizedStringFromTable(name, DLGPlayerLocalizedStringTable, nil); + return [[NSBundle bundleForClass:[self class]] localizedStringForKey:name value:nil table:DLGPlayerLocalizedStringTable]; } + (NSString *)durationStringFromSeconds:(int)seconds { @@ -51,4 +60,24 @@ + (NSString *)durationStringFromSeconds:(int)seconds { return ms; } ++ (BOOL)isMetalSupport { + #if !TARGET_IPHONE_SIMULATOR + if (@available(iOS 9.0, *)) { + if (isMetalSupportChecked) { + return isMetalSupport; + } + + isMetalSupport = MTLCreateSystemDefaultDevice() != nil; + isMetalSupportChecked = YES; + return isMetalSupport; + } + #endif + return NO; +} + ++ (void)setDebugEnabled:(BOOL)enabled { + debugEnabled = enabled; +} + + @end diff --git a/DLGPlayer/frame/DLGPlayerFrame.h b/DLGPlayer/frame/DLGPlayerFrame.h index 97f64e1..1ab8d82 100644 --- a/DLGPlayer/frame/DLGPlayerFrame.h +++ b/DLGPlayer/frame/DLGPlayerFrame.h @@ -16,9 +16,10 @@ typedef enum : NSUInteger { @interface DLGPlayerFrame : NSObject +@property (nonatomic) BOOL dropFrame; @property (nonatomic) DLGPlayerFrameType type; -@property (nonatomic) NSData *data; @property (nonatomic) double position; @property (nonatomic) double duration; +@property (nonatomic) NSData *data; @end diff --git a/DLGPlayer/frame/DLGPlayerVideoFrame.h b/DLGPlayer/frame/DLGPlayerVideoFrame.h index 5748e8c..b79db7d 100644 --- a/DLGPlayer/frame/DLGPlayerVideoFrame.h +++ b/DLGPlayer/frame/DLGPlayerVideoFrame.h @@ -8,6 +8,7 @@ #import "DLGPlayerFrame.h" #import +@import MetalKit; typedef enum : NSUInteger { kDLGPlayerVideoFrameTypeNone, @@ -17,10 +18,14 @@ typedef enum : NSUInteger { @interface DLGPlayerVideoFrame : DLGPlayerFrame +@property (nonatomic, readonly) BOOL prepared; @property (nonatomic) DLGPlayerVideoFrameType videoType; @property (nonatomic) int width; @property (nonatomic) int height; +@property (nonatomic) float brightness; -- (BOOL)prepareRender:(GLuint)program; +- (BOOL)prepareProgram:(GLuint)program; +- (BOOL)prepareDevice:(__weak id)device; +- (BOOL)render:(__weak id)encoder; @end diff --git a/DLGPlayer/frame/DLGPlayerVideoFrame.m b/DLGPlayer/frame/DLGPlayerVideoFrame.m index 1859041..d6b1ccd 100644 --- a/DLGPlayer/frame/DLGPlayerVideoFrame.m +++ b/DLGPlayer/frame/DLGPlayerVideoFrame.m @@ -13,12 +13,25 @@ @implementation DLGPlayerVideoFrame - (id)init { self = [super init]; if (self) { + _brightness = 1; self.type = kDLGPlayerFrameTypeVideo; } return self; } -- (BOOL)prepareRender:(GLuint)program { +- (BOOL)prepared { + return NO; +} + +- (BOOL)prepareProgram:(GLuint)program { + return NO; +} + +- (BOOL)prepareDevice:(__weak id)device { + return NO; +} + +- (BOOL)render:(__weak id)encoder { return NO; } diff --git a/DLGPlayer/frame/DLGPlayerVideoRGBFrame.m b/DLGPlayer/frame/DLGPlayerVideoRGBFrame.m index 656a353..f48c177 100644 --- a/DLGPlayer/frame/DLGPlayerVideoRGBFrame.m +++ b/DLGPlayer/frame/DLGPlayerVideoRGBFrame.m @@ -13,6 +13,7 @@ @interface DLGPlayerVideoRGBFrame () { GLuint _texture; GLint _format; } +@property (nonatomic, strong) id mtlTexture; @end @@ -40,8 +41,14 @@ - (void)deleteTexture { _texture = 0; } } - -- (BOOL)prepareRender:(GLuint)program { + +#pragma mark - Overidden: DLGPlayerVideoFrame + +- (BOOL)prepared { + return (_mtlTexture != nil) || (_texture != 0); +} + +- (BOOL)prepareProgram:(GLuint)program { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if (_texture == 0) { @@ -73,13 +80,59 @@ - (BOOL)prepareRender:(GLuint)program { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _texture); glUniform1i(_sampler, 0); + glUniform1f(glGetUniformLocation(program, "f_brightness"), self.brightness); + + return YES; +} + +- (BOOL)prepareDevice:(__weak id)device { + const NSInteger w = self.width; + const NSInteger h = self.height; + + if (self.data.length != w * h) return NO; + + [self createMTLTextures:device]; + [self updateMTLTextures]; + return YES; +} + +- (BOOL)render:(__weak id)encoder { + if (!self.prepared) { + return NO; + } + + [encoder setTexture:_mtlTexture atIndex:0]; return YES; } +#pragma mark - Public Methods + - (void)setHasAlpha:(BOOL)hasAlpha { _hasAlpha = hasAlpha; _format = hasAlpha ? GL_RGBA : GL_RGB; } + +#pragma mark - Private Methods (Metal) + +- (void)createMTLTextures:(id)device { + if (self.prepared) { + return; + } + + const NSInteger w = self.width; + const NSInteger h = self.height; + + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:w height:h mipmapped:NO]; + + _mtlTexture = [device newTextureWithDescriptor:descriptor]; +} + +- (void)updateMTLTextures { + const NSInteger w = self.width; + const NSInteger h = self.height; + + [_mtlTexture replaceRegion:MTLRegionMake2D(0, 0, w, h) mipmapLevel:0 withBytes:self.data.bytes bytesPerRow:w]; +} @end diff --git a/DLGPlayer/frame/DLGPlayerVideoYUVFrame.m b/DLGPlayer/frame/DLGPlayerVideoYUVFrame.m index 8d6f3cc..053e04b 100644 --- a/DLGPlayer/frame/DLGPlayerVideoYUVFrame.m +++ b/DLGPlayer/frame/DLGPlayerVideoYUVFrame.m @@ -8,19 +8,29 @@ #import "DLGPlayerVideoYUVFrame.h" +struct FilterParamters { + float brightness; +}; + @interface DLGPlayerVideoYUVFrame () { GLint _sampler[3]; GLuint _texture[3]; } - +@property (nonatomic, strong) id yTexture; +@property (nonatomic, strong) id uTexture; +@property (nonatomic, strong) id vTexture; @end @implementation DLGPlayerVideoYUVFrame - + +@synthesize videoType = _videoType; + +#pragma mark - Constructor + - (id)init { self = [super init]; if (self) { - self.videoType = kDLGPlayerVideoFrameTypeYUV; + _videoType = kDLGPlayerVideoFrameTypeYUV; for (int i = 0; i < 3; ++i) { _sampler[i] = -1; _texture[i] = 0; @@ -28,35 +38,20 @@ - (id)init { } return self; } - + +#pragma mark - Destructor + - (void)dealloc { - [self deleteTexture]; + [self deleteTextures]; } -- (void)deleteTexture { - if (_texture[0] != 0) { - glDeleteTextures(3, _texture); - for (int i = 0; i < 3; ++i) _texture[i] = 0; - } -} - -- (BOOL)initSampler:(GLuint)program { - if (_sampler[0] == -1) { - _sampler[0] = glGetUniformLocation(program, "s_texture_y"); - if (_sampler[0] == -1) return NO; - } - if (_sampler[1] == -1) { - _sampler[1] = glGetUniformLocation(program, "s_texture_u"); - if (_sampler[1] == -1) return NO; - } - if (_sampler[2] == -1) { - _sampler[2] = glGetUniformLocation(program, "s_texture_v"); - if (_sampler[2] == -1) return NO; - } - return YES; +#pragma mark - Overidden: DLGPlayerVideoFrame + +- (BOOL)prepared { + return (_yTexture != nil && _uTexture != nil && _vTexture != nil) || (_texture[0] != 0); } - -- (BOOL)prepareRender:(GLuint)program { + +- (BOOL)prepareProgram:(GLuint)program { const int w = self.width; const int h = self.height; @@ -101,7 +96,90 @@ - (BOOL)prepareRender:(GLuint)program { glUniform1i(_sampler[i], i); } + glUniform1f(glGetUniformLocation(program, "f_brightness"), self.brightness); + return YES; } - + +- (BOOL)prepareDevice:(__weak id)device { + const NSInteger w = self.width; + const NSInteger h = self.height; + + if (_Y.length != w * h) return NO; + if (_Cb.length != ((w * h) / 4)) return NO; + if (_Cr.length != ((w * h) / 4)) return NO; + + [self createMTLTextures:device]; + [self updateMTLTextures]; + return YES; +} + +- (BOOL)render:(__weak id)encoder { + if (!self.prepared) { + return NO; + } + + [encoder setTexture:_yTexture atIndex:0]; + [encoder setTexture:_uTexture atIndex:1]; + [encoder setTexture:_vTexture atIndex:2]; + + return YES; +} + +#pragma mark - Private Methods (OpenGL ES) + +- (void)deleteTextures { + if (_texture[0] != 0) { + glDeleteTextures(3, _texture); + for (int i = 0; i < 3; ++i) { + _texture[i] = 0; + } + } +} + +- (BOOL)initSampler:(GLuint)program { + if (_sampler[0] == -1) { + _sampler[0] = glGetUniformLocation(program, "s_texture_y"); + if (_sampler[0] == -1) return NO; + } + if (_sampler[1] == -1) { + _sampler[1] = glGetUniformLocation(program, "s_texture_u"); + if (_sampler[1] == -1) return NO; + } + if (_sampler[2] == -1) { + _sampler[2] = glGetUniformLocation(program, "s_texture_v"); + if (_sampler[2] == -1) return NO; + } + return YES; +} + +#pragma mark - Private Methods (Metal) + +- (void)createMTLTextures:(id)device { + if (self.prepared) { + return; + } + + const NSInteger w = self.width; + const NSInteger h = self.height; + + MTLTextureDescriptor *y = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:w height:h mipmapped:NO]; + MTLTextureDescriptor *u = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:w / 2 height:h / 2 mipmapped:NO]; + MTLTextureDescriptor *v = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Uint width:w / 2 height:h / 2 mipmapped:NO]; + + _yTexture = [device newTextureWithDescriptor:y]; + _uTexture = [device newTextureWithDescriptor:u]; + _vTexture = [device newTextureWithDescriptor:v]; +} + +- (void)updateMTLTextures { + const NSInteger w = self.width; + const NSInteger h = self.height; + + [_yTexture replaceRegion:MTLRegionMake2D(0, 0, w, h) mipmapLevel:0 withBytes:_Y.bytes bytesPerRow:w]; + [_uTexture replaceRegion:MTLRegionMake2D(0, 0, w / 2, h / 2) mipmapLevel:0 withBytes:_Cb.bytes bytesPerRow:w / 2]; + [_vTexture replaceRegion:MTLRegionMake2D(0, 0, w / 2, h / 2) mipmapLevel:0 withBytes:_Cr.bytes bytesPerRow:w / 2]; +} + @end + diff --git a/DLGPlayer/resource/DLGPlayerRGBFragmentShader.glsl b/DLGPlayer/resource/DLGPlayerRGBFragmentShader.glsl index fc71053..2e896f0 100644 --- a/DLGPlayer/resource/DLGPlayerRGBFragmentShader.glsl +++ b/DLGPlayer/resource/DLGPlayerRGBFragmentShader.glsl @@ -1,6 +1,10 @@ varying highp vec2 v_texcoord; +uniform highp float f_brightness; uniform sampler2D s_texture; void main() { - gl_FragColor = texture2D(s_texture, v_texcoord); + highp vec4 texture = texture2D(s_texture, v_texcoord); + highp vec3 black = vec3(0, 0, 0); + + gl_FragColor = vec4(mix(black, texture.rgb, f_brightness), 1); } diff --git a/DLGPlayer/resource/DLGPlayerYUVFragmentShader.glsl b/DLGPlayer/resource/DLGPlayerYUVFragmentShader.glsl index f1c903e..0f8e4f8 100644 --- a/DLGPlayer/resource/DLGPlayerYUVFragmentShader.glsl +++ b/DLGPlayer/resource/DLGPlayerYUVFragmentShader.glsl @@ -1,4 +1,5 @@ varying highp vec2 v_texcoord; +uniform highp float f_brightness; uniform sampler2D s_texture_y; uniform sampler2D s_texture_u; uniform sampler2D s_texture_v; @@ -11,6 +12,8 @@ void main() { highp float r = y + 1.402 * v; highp float g = y - 0.344 * u - 0.714 * v; highp float b = y + 1.772 * u; + highp vec3 rgb = vec3(r, g, b); + highp vec3 black = vec3(0, 0, 0); - gl_FragColor = vec4(r, g, b, 1); + gl_FragColor = vec4(mix(black, rgb, f_brightness), 1); } diff --git a/DLGPlayer/resource/Shader.metal b/DLGPlayer/resource/Shader.metal new file mode 100644 index 0000000..371f5d1 --- /dev/null +++ b/DLGPlayer/resource/Shader.metal @@ -0,0 +1,53 @@ +// +// YCbCr-Shader.metal +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 20/08/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +#include +using namespace metal; + +kernel void YUVColorConversion(constant float *brightness [[buffer(0)]], + texture2d yTexture [[texture(0)]], + texture2d uTexture [[texture(1)]], + texture2d vTexture [[texture(2)]], + texture2d outTexture [[texture(3)]], + uint2 gid [[thread_position_in_grid]]) +{ + float3 colorOffset = float3(0, -0.5, -0.5); + float3x3 colorMatrix = float3x3( + float3(1, 1, 1), + float3(0, -0.344, 1.770), + float3(1.403, -0.714, 0) + ); + + uint2 uvCoords = uint2(gid.x / 2, gid.y / 2); + + float y = yTexture.read(gid).r / 255.0; + float u = uTexture.read(uvCoords).r / 255.0; + float v = vTexture.read(uvCoords).r / 255.0; + + float3 yuv = float3(y, u, v); + float3 rgb = colorMatrix * (yuv + colorOffset); + float3 black = float3(0, 0, 0); + float3 mixed = mix(black, rgb, *brightness); + + outTexture.write(float4(mixed, 1.0), gid); +} + +kernel void RGBColorConversion(constant float *brightness [[buffer(0)]], + texture2d texture [[texture(0)]], + texture2d outTexture [[texture(3)]], + uint2 gid [[thread_position_in_grid]]) +{ + float r = texture.read(gid).r / 255.0; + float g = texture.read(gid).g / 255.0; + float b = texture.read(gid).b / 255.0; + float3 rgb = float3(r, g, b); + float3 black = float3(0, 0, 0); + float3 mixed = mix(black, rgb, *brightness); + + outTexture.write(float4(mixed, 1.0), gid); +} diff --git a/DLGPlayer/view/DLGPlayerVideoFrameView.h b/DLGPlayer/view/DLGPlayerVideoFrameView.h new file mode 100644 index 0000000..09ae0dc --- /dev/null +++ b/DLGPlayer/view/DLGPlayerVideoFrameView.h @@ -0,0 +1,28 @@ +// +// DLGPlayerVideoFrameView.h +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 21/08/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +#ifndef DLGPlayerVideoFrameView_h +#define DLGPlayerVideoFrameView_h + +#import +#import + +@class DLGPlayerVideoFrame; + +@protocol DLGPlayerVideoFrameView +@property (nonatomic) CGSize contentSize; +@property (nonatomic) CGFloat rotation; +@property (nonatomic) BOOL isYUV; +@property (nonatomic) BOOL keepLastFrame; + +- (void)render:(DLGPlayerVideoFrame *)frame; +- (void)clear:(BOOL)forced; +- (UIImage *)snapshot; +@end + +#endif /* DLGPlayerVideoFrameView_h */ diff --git a/DLGPlayer/view/DLGPlayerView.h b/DLGPlayer/view/DLGPlayerView.h index 6b1efc5..7282ea9 100644 --- a/DLGPlayer/view/DLGPlayerView.h +++ b/DLGPlayer/view/DLGPlayerView.h @@ -7,17 +7,10 @@ // #import +#import "DLGPlayerVideoFrameView.h" @class DLGPlayerVideoFrame; -@interface DLGPlayerView : UIView - -@property (nonatomic) CGSize contentSize; -@property (nonatomic) CGFloat rotation; -@property (nonatomic) BOOL isYUV; -@property (nonatomic) BOOL keepLastFrame; - -- (void)render:(DLGPlayerVideoFrame *)frame; -- (void)clear; - +@interface DLGPlayerView : UIView +- (BOOL)setCurrentEAGLContext; @end diff --git a/DLGPlayer/view/DLGPlayerView.m b/DLGPlayer/view/DLGPlayerView.m index e261576..bde6773 100644 --- a/DLGPlayer/view/DLGPlayerView.m +++ b/DLGPlayer/view/DLGPlayerView.m @@ -6,6 +6,7 @@ // Copyright © 2016 Liu Junqi. All rights reserved. // +#import "DLGPlayerUtils.h" #import "DLGPlayerView.h" #import #import @@ -17,7 +18,6 @@ @interface DLGPlayerView () { CAEAGLLayer *_eaglLayer; - EAGLContext *_context; GLuint _frameBuffer; GLuint _renderBuffer; GLuint _programHandle; @@ -43,6 +43,7 @@ @interface DLGPlayerView () { BOOL _isFlipVertical; float _ratio[2]; float _scale[2]; + EAGLContext *_context; } @property (nonatomic, strong) DLGPlayerVideoFrame *lastFrame; @@ -50,6 +51,14 @@ @interface DLGPlayerView () { @end @implementation DLGPlayerView +{ + NSTimeInterval beforeTimeInterval; +} + +@synthesize isYUV = _isYUV; +@synthesize keepLastFrame = _keepLastFrame; +@synthesize rotation = _rotation; +@synthesize contentSize = _contentSize; - (id)init { self = [super init]; @@ -74,7 +83,6 @@ - (id)initWithFrame:(CGRect)frame { } - (void)dealloc { - NSLog(@"DLGPlayerView dealloc"); } + (Class)layerClass { @@ -94,31 +102,40 @@ - (void)reload { [self updatePosition]; [self updateScale]; [self updateRotationMatrix]; - [self render:self.lastFrame]; + [self render:_lastFrame]; } -- (void)clear { - self.keepLastFrame = NO; - self.lastFrame = nil; +- (void)clear:(BOOL)forced { + if (forced || !_keepLastFrame) { + _lastFrame = nil; + } [self render:nil]; } +- (BOOL)setCurrentEAGLContext { + if (!_context || [EAGLContext currentContext] == _context) + return NO; + return [EAGLContext setCurrentContext:_context]; +} + - (BOOL)initVars { _eaglLayer = (CAEAGLLayer *)self.layer; _eaglLayer.opaque = YES; + _eaglLayer.contentsScale = [UIScreen mainScreen].scale; _eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking : @(NO), kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8 }; - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - if (_context == nil) return NO; - if (![EAGLContext setCurrentContext:_context]) return NO; + + if ((_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]) == nil) { + return NO; + } [self initVertex]; [self initTexCord]; [self initProjection]; - self.rotation = 0; - self.keepLastFrame = NO; + _rotation = 0; + _keepLastFrame = NO; return YES; } @@ -167,24 +184,28 @@ - (void)createGLProgram { // Create program GLuint program = glCreateProgram(); if (program == 0) { - NSLog(@"FAILED to create program."); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"FAILED to create program."); + } return; } // Load shaders - NSBundle *bundle = [NSBundle mainBundle]; - NSString *vertexShaderFilename = @"DLGPlayerVertexShader"; - if (_shouldScale) vertexShaderFilename = @"DLGPlayerRotationScaleVertexShader"; - else if (_shouldRotate) vertexShaderFilename = @"DLGPlayerRotationVertexShader"; - NSString *vertexShaderFile = [bundle pathForResource:vertexShaderFilename ofType:@"glsl"]; - GLuint vertexShader = [DLGPlayerView loadShader:GL_VERTEX_SHADER withFile:vertexShaderFile]; - NSString *fragmentShaderResource = _isYUV ? @"DLGPlayerYUVFragmentShader" : @"DLGPlayerRGBFragmentShader"; - NSString *fragmentShaderFile = [bundle pathForResource:fragmentShaderResource ofType:@"glsl"]; - GLuint fragmentShader = [DLGPlayerView loadShader:GL_FRAGMENT_SHADER withFile:fragmentShaderFile]; - - // Attach shaders - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); + @autoreleasepool { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *vertexShaderFilename = @"DLGPlayerVertexShader"; + if (_shouldScale) vertexShaderFilename = @"DLGPlayerRotationScaleVertexShader"; + else if (_shouldRotate) vertexShaderFilename = @"DLGPlayerRotationVertexShader"; + NSString *vertexShaderFile = [bundle pathForResource:vertexShaderFilename ofType:@"glsl"]; + GLuint vertexShader = [DLGPlayerView loadShader:GL_VERTEX_SHADER withFile:vertexShaderFile]; + NSString *fragmentShaderResource = _isYUV ? @"DLGPlayerYUVFragmentShader" : @"DLGPlayerRGBFragmentShader"; + NSString *fragmentShaderFile = [bundle pathForResource:fragmentShaderResource ofType:@"glsl"]; + GLuint fragmentShader = [DLGPlayerView loadShader:GL_FRAGMENT_SHADER withFile:fragmentShaderFile]; + + // Attach shaders + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + } // Bind glBindAttribLocation(program, VERTEX_ATTRIBUTE_POSITION, "position"); @@ -202,7 +223,9 @@ - (void)createGLProgram { if (length > 1) { char *log = malloc(sizeof(char) * length); glGetProgramInfoLog(program, length, NULL, log); - NSLog(@"FAILED to link program, error: %s", log); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"FAILED to link program, error: %s", log); + } free(log); } @@ -289,7 +312,7 @@ - (void)updateScale { - (void)updateRotationMatrix { if (_shouldRotate) { if (_isFlipVertical) { [DLGPlayerView flipVertical:_mat3Rotation]; } - else { [DLGPlayerView rotate:self.rotation matrix:_mat3Rotation]; } + else { [DLGPlayerView rotate:_rotation matrix:_mat3Rotation]; } } if (_shouldScale) { [DLGPlayerView ratio:(GLfloat)_backingWidth/(GLfloat)_backingHeight matrix:_mat3Ratio]; @@ -327,6 +350,10 @@ - (void)setIsYUV:(BOOL)isYUV { } - (void)render:(DLGPlayerVideoFrame *)frame { + if (frame.width < 1 || frame.height < 1) { + return; + } + glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); @@ -336,7 +363,7 @@ - (void)render:(DLGPlayerVideoFrame *)frame { // Set frame glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if ([frame prepareRender:_programHandle]) { + if ([frame prepareProgram:_programHandle]) { glUniformMatrix4fv(_projectionSlot, 1, GL_FALSE, _mat4Projection); if (_shouldRotate) { glUniformMatrix3fv(_rotationSlot, 1, GL_FALSE, _mat3Rotation); @@ -355,7 +382,52 @@ - (void)render:(DLGPlayerVideoFrame *)frame { [_context presentRenderbuffer:GL_RENDERBUFFER]; - if (_keepLastFrame) self.lastFrame = frame; + if (_keepLastFrame) _lastFrame = frame; +} + +/* + * https://developer.apple.com/library/archive/qa/qa1704/_index.html + */ +- (UIImage *)snapshot { + NSInteger width = _backingWidth, height = _backingHeight; + NSInteger dataLength = width * height * 4; + GLubyte *data = (GLubyte*)malloc(dataLength * sizeof(GLubyte)); + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glReadPixels(0, 0, _backingWidth, _backingHeight, GL_RGBA, GL_UNSIGNED_BYTE, data); + + CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, data, dataLength, NULL); + + if (ref == NULL) { + return nil; + } + + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); + CGImageRef iref = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, + ref, NULL, true, kCGRenderingIntentDefault); + + NSInteger widthInPoints, heightInPoints; + CGFloat scale = _eaglLayer.contentsScale; + widthInPoints = width / scale; + heightInPoints = height / scale; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(widthInPoints, heightInPoints), NO, scale); + + CGContextRef cgcontext = UIGraphicsGetCurrentContext(); + + CGContextSetBlendMode(cgcontext, kCGBlendModeCopy); + CGContextDrawImage(cgcontext, CGRectMake(0.0, 0.0, widthInPoints, heightInPoints), iref); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + free(data); + CFRelease(ref); + CFRelease(colorspace); + CGImageRelease(iref); + + return image; } #pragma mark - Utils @@ -363,7 +435,9 @@ + (GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString { // 1. Create shader GLuint shader = glCreateShader(type); if (shader == 0) { - NSLog(@"FAILED to create shader."); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"FAILED to create shader."); + } return 0; } @@ -384,7 +458,9 @@ + (GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString { if (length > 1) { char *log = malloc(sizeof(char) * length); glGetShaderInfoLog(shader, length, NULL, log); - NSLog(@"FAILED to compile shader, error: %s", log); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"FAILED to compile shader, error: %s", log); + } free(log); } glDeleteShader(shader); @@ -398,7 +474,9 @@ + (GLuint)loadShader:(GLenum)type withFile:(NSString *)shaderFile { NSError *error = nil; NSString *shaderString = [NSString stringWithContentsOfFile:shaderFile encoding:NSUTF8StringEncoding error:&error]; if (shaderString == nil) { - NSLog(@"FAILED to load shader file: %@, Error: %@", shaderFile, error); + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"FAILED to load shader file: %@, Error: %@", shaderFile, error); + } return 0; } diff --git a/DLGPlayer/view/MetalPlayerView.h b/DLGPlayer/view/MetalPlayerView.h new file mode 100644 index 0000000..39fb6e2 --- /dev/null +++ b/DLGPlayer/view/MetalPlayerView.h @@ -0,0 +1,18 @@ +// +// MetalPlayerView.h +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 20/08/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +#import +#import "DLGPlayerVideoFrameView.h" + +NS_ASSUME_NONNULL_BEGIN + +API_AVAILABLE(ios(9.0)) +@interface MetalPlayerView : UIView +@end + +NS_ASSUME_NONNULL_END diff --git a/DLGPlayer/view/MetalPlayerView.m b/DLGPlayer/view/MetalPlayerView.m new file mode 100644 index 0000000..2ec537a --- /dev/null +++ b/DLGPlayer/view/MetalPlayerView.m @@ -0,0 +1,215 @@ +// +// MetalPlayerView.m +// DLGPlayer +// +// Created by KWANG HYOUN KIM on 20/08/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +@import MetalKit; +#import "MetalPlayerView.h" +#import "DLGPlayerUtils.h" +#import "DLGPlayerVideoFrame.h" +#import "DLGPlayerVideoYUVFrame.h" + +@interface MetalPlayerView () +@property (nonatomic, readonly) BOOL isRenderingAvailable; +@end + +@implementation MetalPlayerView +{ +@private + MTLSize threadsPerThreadgroup; + MTLSize threadgroupsPerGrid; + id defaultLibrary; + id commandQueue; + id pipelineState; + MTKView *metalView; + DLGPlayerVideoFrame *currentFrame; +} + +@synthesize isYUV = _isYUV; +@synthesize keepLastFrame = _keepLastFrame; +@synthesize rotation = _rotation; +@synthesize contentSize = _contentSize; + +#pragma mark - Constructors + +- (void)dealloc { + if (DLGPlayerUtils.debugEnabled) { + NSLog(@"MetalPlayerView dealloc"); + } + [self clear:YES]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self initProperties]; + } + return self; +} + +#pragma mark - Private Properties + +- (BOOL)isRenderingAvailable { + return metalView.currentDrawable != nil && currentFrame != nil && currentFrame.prepared; +} + +#pragma mark - Overridden: UIView + +- (void)layoutSubviews { + [super layoutSubviews]; + + metalView.frame = self.bounds; +} + +#if TARGET_IPHONE_SIMULATOR +#else + +#pragma mark - Private Methods + +- (void)setUpPipelineState { + NSString *name = _isYUV ? @"YUVColorConversion" : @"RGBColorConversion"; + + id kernelFunction = [defaultLibrary newFunctionWithName:name]; + + if (!kernelFunction && DLGPlayerUtils.debugEnabled) { + NSLog(@"Error creating compute shader"); + return; + } + + pipelineState = [metalView.device newComputePipelineStateWithFunction:kernelFunction error:nil]; + + if (!pipelineState && DLGPlayerUtils.debugEnabled) { + NSLog(@"Error creating the pipeline state"); + } +} + +#pragma mark - Private Methods (MTKViewDelegate) + +- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {} + +- (void)drawInMTKView:(nonnull MTKView *)view { + if (!self.isRenderingAvailable || !pipelineState) { + return; + } + + id commandBuffer = [commandQueue commandBuffer]; + + if (!commandBuffer) { + return; + } + + id commandEncoder = [commandBuffer computeCommandEncoder]; + + if (commandEncoder) { + float brightness = currentFrame.brightness; + + [commandEncoder setComputePipelineState:pipelineState]; + [commandEncoder setBytes:&brightness length:sizeof(brightness) atIndex:0]; + [currentFrame render:commandEncoder]; + [commandEncoder setTexture:metalView.currentDrawable.texture atIndex:3]; + [commandEncoder dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup]; + [commandEncoder endEncoding]; + + [commandBuffer presentDrawable:metalView.currentDrawable]; + [commandBuffer commit]; + } +} + +#endif + +- (void)initProperties { + metalView = [[MTKView alloc] init]; + metalView.autoResizeDrawable = NO; + metalView.framebufferOnly = NO; + metalView.contentScaleFactor = UIScreen.mainScreen.scale; + metalView.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + metalView.clearColor = MTLClearColorMake(1, 1, 1, 1); + metalView.device = MTLCreateSystemDefaultDevice(); + metalView.delegate = self; + + [self addSubview:metalView]; + +#if TARGET_IPHONE_SIMULATOR + NSLog(@"[DLGPlayer] Metal will not work on simulator."); +#endif + commandQueue = [metalView.device newCommandQueue]; + + if (@available(iOS 10.0, *)) { + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + defaultLibrary = [metalView.device newDefaultLibraryWithBundle:bundle error:nil]; + } else { + defaultLibrary = [metalView.device newDefaultLibrary]; + } + + threadsPerThreadgroup = MTLSizeMake(16, 16, 1); + threadgroupsPerGrid = MTLSizeMake(3840 / threadsPerThreadgroup.width, 2160 / threadsPerThreadgroup.height, 1); +} + +#pragma mark - Public Methods (DLGPlayerVideoFrame) + +- (void)setContentSize:(CGSize)contentSize { + _contentSize = contentSize; +} + +- (void)setRotation:(CGFloat)rotation { + _rotation = rotation; +} + +- (void)setIsYUV:(BOOL)isYUV { + if (isYUV == _isYUV) { + return; + } + + _isYUV = isYUV; +#if TARGET_IPHONE_SIMULATOR +#else + [self setUpPipelineState]; +#endif +} + +- (void)clear:(BOOL)forced { + if (forced || !_keepLastFrame) { + currentFrame = nil; + } + + [metalView releaseDrawables]; +} + +- (void)render:(DLGPlayerVideoFrame *)frame { + if (frame == nil || frame.width < 1 || frame.height < 1) { + return; + } + + currentFrame = frame; + metalView.drawableSize = CGSizeMake(frame.width, frame.height); + + if ([frame prepareDevice:metalView.device]) { + // TODO: - Impl scale / flip / rotation. + } +} + +- (UIImage *)snapshot { +#if TARGET_IPHONE_SIMULATOR + NSLog(@"[DLGPlayer] Metal will not work to make snapshot on simulator."); + return nil; +#else + if (!self.isRenderingAvailable) { + return nil; + } + + const id texture = metalView.currentDrawable.texture; + const NSInteger w = texture.width; + const NSInteger h = texture.height; + CIContext *context = [CIContext contextWithMTLDevice:metalView.device]; + CIImage *outputImage = [[CIImage alloc] initWithMTLTexture:texture options:@{kCIImageColorSpace: (__bridge_transfer id) CGColorSpaceCreateDeviceRGB()}]; + CGImageRef cgImg = [context createCGImage:outputImage fromRect:CGRectMake(0, 0, w, h)]; + UIImage *resultImg = [UIImage imageWithCGImage:cgImg scale:UIScreen.mainScreen.scale orientation:UIImageOrientationDownMirrored]; + CGImageRelease(cgImg); + return resultImg; +#endif +} + +@end diff --git a/DLGPlayerDemo/AppDelegate.swift b/DLGPlayerDemo/AppDelegate.swift new file mode 100644 index 0000000..ddc092a --- /dev/null +++ b/DLGPlayerDemo/AppDelegate.swift @@ -0,0 +1,50 @@ +// +// AppDelegate.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 13/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationDidReceiveMemoryWarning(_ application: UIApplication) { + print("applicationDidReceiveMemoryWarning"); + } + + func applicationWillResignActive(_ application: UIApplication) { + // 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. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // 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. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DLGPlayerDemo/Assets.xcassets/Contents.json b/DLGPlayerDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/DLGPlayerDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard b/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..bfa3612 --- /dev/null +++ b/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DLGPlayerDemo/Base.lproj/Main.storyboard b/DLGPlayerDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..c41ac95 --- /dev/null +++ b/DLGPlayerDemo/Base.lproj/Main.storyboard @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h b/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h new file mode 100644 index 0000000..34781b4 --- /dev/null +++ b/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h @@ -0,0 +1,14 @@ +// +// DLGPlayerDemo-Bridging-Header.h +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#ifndef DLGPlayerDemo_Bridging_Header_h +#define DLGPlayerDemo_Bridging_Header_h + +@import DLGPlayer; + +#endif /* DLGPlayerDemo_Bridging_Header_h */ diff --git a/DLGPlayerDemo/Info.plist b/DLGPlayerDemo/Info.plist new file mode 100644 index 0000000..9671f43 --- /dev/null +++ b/DLGPlayerDemo/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/DLGPlayerDemo/RootViewController.swift b/DLGPlayerDemo/RootViewController.swift new file mode 100644 index 0000000..fce6e0a --- /dev/null +++ b/DLGPlayerDemo/RootViewController.swift @@ -0,0 +1,203 @@ +// +// RootViewController.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +import UIKit + +final class RootViewController: UIViewController { + + @IBOutlet private weak var containerView: UIView! + @IBOutlet private weak var muteButton: UIButton! + @IBOutlet private weak var playOrPauseButton: UIButton! + @IBOutlet private weak var segmentedControl: UISegmentedControl! + + private lazy var players = [DLGSimplePlayerViewController]() + + private var isFirstViewAppearance = true + + deinit { + print("RootViewController deinit") + navigationItem.rightBarButtonItem = nil + } + + private func createPlayers() { + for i in 0..<1 { + let pv = DLGSimplePlayerViewController() + pv.view.translatesAutoresizingMaskIntoConstraints = true + pv.delegate = self + pv.isAllowsFrameDrop = true + pv.isAutoplay = true +// pv.isMute = true + pv.preventFromScreenLock = true + pv.restorePlayAfterAppEnterForeground = true + pv.minBufferDuration = 0 + pv.maxBufferDuration = 5 + pv.view.backgroundColor = .red + + addChild(pv) + + let height = 9 * containerView.frame.width / 16 + + pv.view.frame = .init(x: 0, y: height * CGFloat(i), width: containerView.frame.width, height: height) + containerView.addSubview(pv.view) + players.append(pv) + } + } + private func removePlayers() { + players.forEach { + $0.stopCompletely() + $0.removeFromParent() + $0.view.removeFromSuperview() + } + players.removeAll() + } + private func playAll() { + players.forEach { +// $0.url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" + $0.url = "rtmps://devmedia011.toastcam.com:10082/flvplayback/AAAAAADJMO?token=b6e503e4-f47c-4238-baca-51cbdfc10001" + $0.open() + } + } + private func reset() { + removePlayers() + createPlayers() + playAll() + } + + override func viewDidLoad() { + super.viewDidLoad() + + DLGPlayerUtils.setDebugEnabled(true) + + for i in 0.. ", viewController.url) + } + func viewController(_ viewController: DLGSimplePlayerViewController, didReceiveError error: Error) { +// print("didReceiveError -> ", error) + } + func viewController(_ viewController: DLGSimplePlayerViewController, didChange status: DLGPlayerStatus) { +// print("didChange", viewController.hash, status.stringValue) + playOrPauseButton.isSelected = viewController.controlStatus.playing + muteButton.isSelected = !viewController.isMute + } +} + +extension DLGPlayerStatus { + var stringValue: String { + switch self { + case .buffering: + return "buffering" + case .closed: + return "closed" + case .audioClosed: + return "audioClosed" + case .closing: + return "closing" + case .EOF: + return "EOF" + case .none: + return "none" + case .opened: + return "opened" + case .audioOpened: + return "audioOpened" + case .opening: + return "opening" + case .paused: + return "paused" + case .playing: + return "playing" + } + } +} diff --git a/DLGPlayerDemo/SecondViewController.swift b/DLGPlayerDemo/SecondViewController.swift new file mode 100644 index 0000000..5cb9341 --- /dev/null +++ b/DLGPlayerDemo/SecondViewController.swift @@ -0,0 +1,168 @@ +// +// SecondViewController.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 31/05/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +import UIKit + +class SecondViewController: UIViewController { + + @IBOutlet private weak var muteButton: UIButton! + @IBOutlet private weak var playOrPauseButton: UIButton! + + private var isFirstViewAppearance = true + private var playerViewController: DLGSimplePlayerViewController? { + didSet { + playerViewController.map { + $0.delegate = self + $0.isAllowsFrameDrop = true + $0.isAutoplay = true + $0.isMute = false + $0.preventFromScreenLock = true + $0.restorePlayAfterAppEnterForeground = true + $0.minBufferDuration = 0 + $0.maxBufferDuration = 3 + } + } + } + + deinit { + + + print("deinit") + + } + + override func viewDidLoad() { + super.viewDidLoad() + + // navigationItem.leftBarButtonItem = .init(title: "close", style: .plain, target: self, action: #selector(leftBarButtonItemClicked)) + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // if isFirstViewAppearance { + playRTMP() + // } + } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + isFirstViewAppearance = false + } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + playerViewController?.stop() + } + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + switch segue.destination { + case let vc as DLGSimplePlayerViewController: + playerViewController = vc + default: () + } + } + + // MARK: - Play Test + + private func playDownload() { + playerViewController?.url = "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4" + playerViewController?.open() + } + private func playRTMP() { + playerViewController?.url = "rtmps://devmedia010.toastcam.com:10082/flvplayback/AAAAAACYMJ?token=b6e503e4-f47c-4238-baca-51cbdfc10001&time=1571796156306" + playerViewController?.open() + } + + // MARK: - Hard Test + + private let hardTestCount: Int = 10 + private var playCount: Int = 0 + private func startHardTest() { + if #available(iOS 10.0, *), playCount < hardTestCount { + var count = 0 + + Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { + self.playTest(0) + count += 1 + + if count > self.hardTestCount { + $0.invalidate() + } + } + } + } + private func playTest(_ count: Int) { + let url = count % 2 == 0 ? + "rtmps://devmedia011.toastcam.com:10082/flvplayback/AAAAAACPUS?token=1234567890" : + "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4" + + playerViewController?.stop() + + // print("[playTest] ------------------------------------------------------------------------------------") + // print("[playTest] will open -> ", url) + playerViewController?.url = url + playerViewController?.open() + // print("[playTest] opening -> ", playerViewController?.url) + } + + // MARK: - Private Selectors + + @IBAction private func captureButtonClicked() { + playerViewController?.player.snapshot() + .map { UIImageView(image: $0) } + .map { [weak self] in + self?.view.addSubview($0) + } + } + @IBAction private func closeButtonClicked() { + dismiss(animated: true) + } + @IBAction private func leftBarButtonItemClicked() { + dismiss(animated: true) + } + @IBAction private func muteButtonClicked(_ sender: UIButton) { + sender.isSelected = !sender.isSelected + playerViewController?.isMute = !sender.isSelected + } + @IBAction private func playOrPauseButtonClicked(_ sender: UIButton) { + sender.isSelected = !sender.isSelected + + if sender.isSelected { + if playerViewController?.status == .paused { + playerViewController?.play() + } else { + playRTMP() + } + } else { + playerViewController?.pause() + } + } + @IBAction private func refreshButtonClicked(_ sender: UIButton) { + playerViewController?.stop() + playRTMP() + } + @IBAction private func stopButtonClicked() { + playerViewController?.stop() + } + @IBAction private func valueChanged(_ sender: UISlider) { + playerViewController?.player.brightness = sender.value + } +} + +extension SecondViewController: DLGSimplePlayerViewControllerDelegate { + func didBeginRender(in viewController: DLGSimplePlayerViewController) { +// print("didBeginRender -> ", viewController.url) + } + func viewController(_ viewController: DLGSimplePlayerViewController, didReceiveError error: Error) { +// print("didReceiveError -> ", error) + } + func viewController(_ viewController: DLGSimplePlayerViewController, didChange status: DLGPlayerStatus) { + // print("didChange", viewController.hash, status.stringValue) + playOrPauseButton.isSelected = viewController.controlStatus.playing + muteButton.isSelected = !viewController.isMute + } +} diff --git a/DLGPlayerDemo/ViewController.swift b/DLGPlayerDemo/ViewController.swift new file mode 100644 index 0000000..0de77a0 --- /dev/null +++ b/DLGPlayerDemo/ViewController.swift @@ -0,0 +1,31 @@ +// +// ViewController.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 05/12/2019. +// Copyright © 2019 KWANG HYOUN KIM. All rights reserved. +// + +import Foundation + +class ViewController: UIViewController { + + deinit { + navigationItem.rightBarButtonItem = nil + } + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.rightBarButtonItem = .init(title: "open", style: .plain, target: self, action: #selector(clicked)) + clicked() + } + + @objc private func clicked() { + guard let vc = storyboard?.instantiateViewController(withIdentifier: "RootViewController") else { + return + } + + navigationController?.pushViewController(vc, animated: false) + } +} diff --git a/Example-Carthage/Cartfile b/Example-Carthage/Cartfile new file mode 100755 index 0000000..f029f25 --- /dev/null +++ b/Example-Carthage/Cartfile @@ -0,0 +1,2 @@ + +github "pisces/DLGPlayer" "master" diff --git a/Example-Carthage/Cartfile.resolved b/Example-Carthage/Cartfile.resolved new file mode 100644 index 0000000..b9b51bf --- /dev/null +++ b/Example-Carthage/Cartfile.resolved @@ -0,0 +1 @@ +github "pisces/DLGPlayer" "dfe51a7e44cdf8333396c1f61e33befbd4953e8b" diff --git a/Example-Carthage/DLGPlayerDemo.xcodeproj/project.pbxproj b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bcb05a2 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.pbxproj @@ -0,0 +1,509 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 2354099121BA044100533B9B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2354099021BA044100533B9B /* AppDelegate.swift */; }; + 2354099321BA044100533B9B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2354099221BA044100533B9B /* ViewController.swift */; }; + 2354099621BA044100533B9B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2354099421BA044100533B9B /* Main.storyboard */; }; + 2354099821BA044200533B9B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2354099721BA044200533B9B /* Assets.xcassets */; }; + 2354099B21BA044200533B9B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2354099921BA044200533B9B /* LaunchScreen.storyboard */; }; + 235409A621BA044300533B9B /* DLGPlayerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 235409A521BA044300533B9B /* DLGPlayerDemoTests.swift */; }; + 235409B221BA04B800533B9B /* DLGPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 235409B121BA04B800533B9B /* DLGPlayer.framework */; }; + 235409BF21BA0A2D00533B9B /* DLGPlayer.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 235409B121BA04B800533B9B /* DLGPlayer.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 235409A221BA044300533B9B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 2354098521BA044100533B9B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2354098C21BA044100533B9B; + remoteInfo = DLGPlayerDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 235409BE21BA0A2100533B9B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 235409BF21BA0A2D00533B9B /* DLGPlayer.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 2354098D21BA044100533B9B /* DLGPlayerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DLGPlayerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 2354099021BA044100533B9B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 2354099221BA044100533B9B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 2354099521BA044100533B9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 2354099721BA044200533B9B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 2354099A21BA044200533B9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 2354099C21BA044200533B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 235409A121BA044200533B9B /* DLGPlayerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DLGPlayerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 235409A521BA044300533B9B /* DLGPlayerDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DLGPlayerDemoTests.swift; sourceTree = ""; }; + 235409A721BA044300533B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 235409B121BA04B800533B9B /* DLGPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DLGPlayer.framework; path = Carthage/Build/iOS/DLGPlayer.framework; sourceTree = ""; }; + 235409B321BA04FA00533B9B /* DLGPlayerDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DLGPlayerDemo-Bridging-Header.h"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2354098A21BA044100533B9B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 235409B221BA04B800533B9B /* DLGPlayer.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2354099E21BA044200533B9B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2354098421BA044100533B9B = { + isa = PBXGroup; + children = ( + 2354098F21BA044100533B9B /* DLGPlayerDemo */, + 235409A421BA044300533B9B /* DLGPlayerDemoTests */, + 2354098E21BA044100533B9B /* Products */, + 235409B021BA04B700533B9B /* Frameworks */, + ); + sourceTree = ""; + }; + 2354098E21BA044100533B9B /* Products */ = { + isa = PBXGroup; + children = ( + 2354098D21BA044100533B9B /* DLGPlayerDemo.app */, + 235409A121BA044200533B9B /* DLGPlayerDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 2354098F21BA044100533B9B /* DLGPlayerDemo */ = { + isa = PBXGroup; + children = ( + 2354099021BA044100533B9B /* AppDelegate.swift */, + 2354099221BA044100533B9B /* ViewController.swift */, + 2354099421BA044100533B9B /* Main.storyboard */, + 2354099721BA044200533B9B /* Assets.xcassets */, + 2354099921BA044200533B9B /* LaunchScreen.storyboard */, + 2354099C21BA044200533B9B /* Info.plist */, + 235409B321BA04FA00533B9B /* DLGPlayerDemo-Bridging-Header.h */, + ); + path = DLGPlayerDemo; + sourceTree = ""; + }; + 235409A421BA044300533B9B /* DLGPlayerDemoTests */ = { + isa = PBXGroup; + children = ( + 235409A521BA044300533B9B /* DLGPlayerDemoTests.swift */, + 235409A721BA044300533B9B /* Info.plist */, + ); + path = DLGPlayerDemoTests; + sourceTree = ""; + }; + 235409B021BA04B700533B9B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 235409B121BA04B800533B9B /* DLGPlayer.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2354098C21BA044100533B9B /* DLGPlayerDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 235409AA21BA044300533B9B /* Build configuration list for PBXNativeTarget "DLGPlayerDemo" */; + buildPhases = ( + 2354098921BA044100533B9B /* Sources */, + 2354098A21BA044100533B9B /* Frameworks */, + 2354098B21BA044100533B9B /* Resources */, + 235409BE21BA0A2100533B9B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DLGPlayerDemo; + productName = DLGPlayerDemo; + productReference = 2354098D21BA044100533B9B /* DLGPlayerDemo.app */; + productType = "com.apple.product-type.application"; + }; + 235409A021BA044200533B9B /* DLGPlayerDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 235409AD21BA044300533B9B /* Build configuration list for PBXNativeTarget "DLGPlayerDemoTests" */; + buildPhases = ( + 2354099D21BA044200533B9B /* Sources */, + 2354099E21BA044200533B9B /* Frameworks */, + 2354099F21BA044200533B9B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 235409A321BA044300533B9B /* PBXTargetDependency */, + ); + name = DLGPlayerDemoTests; + productName = DLGPlayerDemoTests; + productReference = 235409A121BA044200533B9B /* DLGPlayerDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2354098521BA044100533B9B /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = "KWANG HYOUN KIM"; + TargetAttributes = { + 2354098C21BA044100533B9B = { + CreatedOnToolsVersion = 10.1; + }; + 235409A021BA044200533B9B = { + CreatedOnToolsVersion = 10.1; + TestTargetID = 2354098C21BA044100533B9B; + }; + }; + }; + buildConfigurationList = 2354098821BA044100533B9B /* Build configuration list for PBXProject "DLGPlayerDemo" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 2354098421BA044100533B9B; + productRefGroup = 2354098E21BA044100533B9B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2354098C21BA044100533B9B /* DLGPlayerDemo */, + 235409A021BA044200533B9B /* DLGPlayerDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2354098B21BA044100533B9B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2354099B21BA044200533B9B /* LaunchScreen.storyboard in Resources */, + 2354099821BA044200533B9B /* Assets.xcassets in Resources */, + 2354099621BA044100533B9B /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2354099F21BA044200533B9B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2354098921BA044100533B9B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2354099321BA044100533B9B /* ViewController.swift in Sources */, + 2354099121BA044100533B9B /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2354099D21BA044200533B9B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 235409A621BA044300533B9B /* DLGPlayerDemoTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 235409A321BA044300533B9B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2354098C21BA044100533B9B /* DLGPlayerDemo */; + targetProxy = 235409A221BA044300533B9B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 2354099421BA044100533B9B /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 2354099521BA044100533B9B /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 2354099921BA044200533B9B /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 2354099A21BA044200533B9B /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 235409A821BA044300533B9B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 235409A921BA044300533B9B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 235409AB21BA044300533B9B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = F5NU2PA3PK; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = DLGPlayerDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = example.DLGPlayerDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "${PROJECT_NAME}/DLGPlayerDemo-Bridging-Header.h"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 235409AC21BA044300533B9B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = F5NU2PA3PK; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + INFOPLIST_FILE = DLGPlayerDemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = example.DLGPlayerDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "${PROJECT_NAME}/DLGPlayerDemo-Bridging-Header.h"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 235409AE21BA044300533B9B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = DLGPlayerDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = example.DLGPlayerDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DLGPlayerDemo.app/DLGPlayerDemo"; + }; + name = Debug; + }; + 235409AF21BA044300533B9B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = DLGPlayerDemoTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = example.DLGPlayerDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DLGPlayerDemo.app/DLGPlayerDemo"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2354098821BA044100533B9B /* Build configuration list for PBXProject "DLGPlayerDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 235409A821BA044300533B9B /* Debug */, + 235409A921BA044300533B9B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 235409AA21BA044300533B9B /* Build configuration list for PBXNativeTarget "DLGPlayerDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 235409AB21BA044300533B9B /* Debug */, + 235409AC21BA044300533B9B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 235409AD21BA044300533B9B /* Build configuration list for PBXNativeTarget "DLGPlayerDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 235409AE21BA044300533B9B /* Debug */, + 235409AF21BA044300533B9B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 2354098521BA044100533B9B /* Project object */; +} diff --git a/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..d849187 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example-Carthage/DLGPlayerDemo/AppDelegate.swift b/Example-Carthage/DLGPlayerDemo/AppDelegate.swift new file mode 100644 index 0000000..449ec9b --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // 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. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // 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. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/Example-Carthage/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example-Carthage/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example-Carthage/DLGPlayerDemo/Assets.xcassets/Contents.json b/Example-Carthage/DLGPlayerDemo/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example-Carthage/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard b/Example-Carthage/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..42a6734 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example-Carthage/DLGPlayerDemo/Base.lproj/Main.storyboard b/Example-Carthage/DLGPlayerDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..9b0f7a3 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/Base.lproj/Main.storyboard @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example-Carthage/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h b/Example-Carthage/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h new file mode 100644 index 0000000..34781b4 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/DLGPlayerDemo-Bridging-Header.h @@ -0,0 +1,14 @@ +// +// DLGPlayerDemo-Bridging-Header.h +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +#ifndef DLGPlayerDemo_Bridging_Header_h +#define DLGPlayerDemo_Bridging_Header_h + +@import DLGPlayer; + +#endif /* DLGPlayerDemo_Bridging_Header_h */ diff --git a/Example-Carthage/DLGPlayerDemo/Info.plist b/Example-Carthage/DLGPlayerDemo/Info.plist new file mode 100644 index 0000000..16be3b6 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example-Carthage/DLGPlayerDemo/ViewController.swift b/Example-Carthage/DLGPlayerDemo/ViewController.swift new file mode 100644 index 0000000..8e8b6f3 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemo/ViewController.swift @@ -0,0 +1,72 @@ +// +// ViewController.swift +// DLGPlayerDemo +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + private var timer: Timer? + private var playerViewController: DLGSimplePlayerViewController! { + didSet { + playerViewController.delegate = self + playerViewController.autoplay = true + playerViewController.repeat = true + playerViewController.preventFromScreenLock = true + playerViewController.restorePlayAfterAppEnterForeground = true + } + } + + private static var num: Int = 11 + + override func viewDidLoad() { + super.viewDidLoad() + + playerViewController.url = "rtmps://devmedia011.toastcam.com:10082/flvplayback/AAAAAACNZM?token=1234567890" + playerViewController.player.minBufferDuration = 1 + playerViewController.player.audio.volume = 1 + playerViewController.open() + } + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + switch segue.destination { + case let vc as DLGSimplePlayerViewController: + playerViewController = vc + default: () + } + } + + private func startTimer() { + stopTimer() + timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerCompletion), userInfo: nil, repeats: true) + } + private func stopTimer() { + timer?.invalidate() + timer = nil + } + + @objc private func timerCompletion() { +// print("player.position", playerViewController.player.position) + } +} + +extension ViewController: DLGSimplePlayerViewControllerDelegate { + func viewController(_ viewController: DLGSimplePlayerViewController, didReceiveError error: Error) { + print("didReceiveError", error) + } + func viewController(_ viewController: DLGSimplePlayerViewController, didChange status: DLGPlayerStatus) { + print("didChange", status.rawValue) + + switch status { + case .opened: + startTimer() + case .closed: + stopTimer() +// case .playing: +// print("player.audio.volume", playerViewController.player.audio.volume) + default: () + } + } +} diff --git a/Example-Carthage/DLGPlayerDemoTests/DLGPlayerDemoTests.swift b/Example-Carthage/DLGPlayerDemoTests/DLGPlayerDemoTests.swift new file mode 100644 index 0000000..db500b1 --- /dev/null +++ b/Example-Carthage/DLGPlayerDemoTests/DLGPlayerDemoTests.swift @@ -0,0 +1,34 @@ +// +// DLGPlayerDemoTests.swift +// DLGPlayerDemoTests +// +// Created by KWANG HYOUN KIM on 07/12/2018. +// Copyright © 2018 KWANG HYOUN KIM. All rights reserved. +// + +import XCTest +@testable import DLGPlayerDemo + +class DLGPlayerDemoTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Example-Carthage/DLGPlayerDemoTests/Info.plist b/Example-Carthage/DLGPlayerDemoTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/Example-Carthage/DLGPlayerDemoTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Example-Carthage/carthage_build.sh b/Example-Carthage/carthage_build.sh new file mode 100644 index 0000000..b8c5ef3 --- /dev/null +++ b/Example-Carthage/carthage_build.sh @@ -0,0 +1 @@ +XCODE_XCCONFIG_FILE="Carthage.xcconfig" carthage build diff --git a/Example-Carthage/carthage_update.sh b/Example-Carthage/carthage_update.sh new file mode 100644 index 0000000..58986b5 --- /dev/null +++ b/Example-Carthage/carthage_update.sh @@ -0,0 +1 @@ +XCODE_XCCONFIG_FILE="Carthage.xcconfig" carthage update diff --git a/Example/DLGPlayer.xcodeproj/project.pbxproj b/Example/DLGPlayer.xcodeproj/project.pbxproj index 18be8a4..f7db0f5 100644 --- a/Example/DLGPlayer.xcodeproj/project.pbxproj +++ b/Example/DLGPlayer.xcodeproj/project.pbxproj @@ -1116,7 +1116,7 @@ "$(PROJECT_DIR)/DLGPlayer/Externals/ffmpeg/lib", "$(PROJECT_DIR)/DLGPlayer/Externals/openssl/lib", ); - ONLY_ACTIVE_ARCH = YES; + ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = cn.devileo.DLGPlayer; PRODUCT_NAME = "$(TARGET_NAME)"; VALID_ARCHS = "arm64 armv7 armv7s"; diff --git a/Example/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/DLGPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme b/Example/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme new file mode 100644 index 0000000..fe5f5f9 --- /dev/null +++ b/Example/DLGPlayer.xcodeproj/xcshareddata/xcschemes/DLGPlayer.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DLGPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/DLGPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json index 1d060ed..d8db8d6 100644 --- a/Example/DLGPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Example/DLGPlayer/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -84,6 +84,11 @@ "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/Example/DLGPlayer/Base.lproj/Main.storyboard b/Example/DLGPlayer/Base.lproj/Main.storyboard index 728cac6..948f346 100644 --- a/Example/DLGPlayer/Base.lproj/Main.storyboard +++ b/Example/DLGPlayer/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -61,11 +61,11 @@ - + - + @@ -85,7 +85,7 @@ - + @@ -94,17 +94,17 @@ - + - + - + diff --git a/Example/DLGPlayer/Externals/ffmpeg/.gitignore b/Example/DLGPlayer/Externals/ffmpeg/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index 4dce2f0..19062ae 100644 --- a/README.md +++ b/README.md @@ -144,5 +144,28 @@ See ***DLGPlayerViewController*** class for more usage details. Thank you all! -## 7. License +## 7. Installation + +### Carthage + +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate DLGPlayer into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "pisces/DLGPlayer" "master" +``` + +Run `XCODE_XCCONFIG_FILE="Carthage.xcconfig" carthage update` to build the framework and drag the built `DLGPlayer.framework` into your Xcode project. + +Cathage building will build ffmpeg, ogg and speex automacally. + +## 8. License See [LICENSE](https://github.com/DeviLeo/DLGPlayer/blob/master/LICENSE "LGPL-3.0"). diff --git a/libs/build-ffmpeg.sh b/libs/build-ffmpeg.sh new file mode 100755 index 0000000..bfa3b74 --- /dev/null +++ b/libs/build-ffmpeg.sh @@ -0,0 +1,154 @@ +#!/bin/sh + +# directories +FF_VERSION="4.1" +#FF_VERSION="snapshot-git" +if [[ $FFMPEG_VERSION != "" ]]; then + FF_VERSION=$FFMPEG_VERSION +fi +SOURCE=`pwd`/"build/src/ffmpeg" +FAT=`pwd`/"build/universal" +THIN=`pwd`/"build/thin" +FFMPEG=$THIN/ffmpeg + +CONFIGURE_FLAGS="--enable-cross-compile --enable-static --disable-shared --disable-debug --disable-programs \ + --disable-doc --enable-pic --enable-neon --enable-optimizations --enable-small" +ARCHS="armv7 armv7s arm64 i386 x86_64" +COMPILE="y" +LIPO="y" +DEPLOYMENT_TARGET="8.0" + +if [ "$*" ] +then + if [ "$*" = "lipo" ] + then + # skip compile + COMPILE= + else + ARCHS="$*" + if [ $# -eq 1 ] + then + # skip lipo + LIPO= + fi + fi +fi + +if [ "$COMPILE" ] +then + if [ ! `which yasm` ] + then + echo 'Yasm not found' + if [ ! `which brew` ] + then + echo 'Homebrew not found. Trying to install...' + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" \ + || exit 1 + fi + echo 'Trying to install Yasm...' + brew install yasm || exit 1 + fi + + HAS_SPEEX=shell pkg-config --exists speex + if [ ! HAS_SPEEX ]; then + brew install speex || exit 1 + fi + + if [ ! `which gas-preprocessor.pl` ] + then + echo 'gas-preprocessor.pl not found. Trying to install...' + (curl -L https://github.com/libav/gas-preprocessor/raw/master/gas-preprocessor.pl \ + -o /usr/local/bin/gas-preprocessor.pl \ + && chmod +x /usr/local/bin/gas-preprocessor.pl) \ + || exit 1 + fi + + if [ ! -r $SOURCE ] + then + mkdir -p "$SOURCE" + cd $SOURCE + echo 'FFmpeg source not found. Trying to download...' + curl http://www.ffmpeg.org/releases/ffmpeg-$FF_VERSION.tar.bz2 | tar xj \ + || exit 1 + cd - + fi + + CWD=`pwd` + for ARCH in $ARCHS + do + echo "building $ARCH..." + mkdir -p "$SOURCE/$ARCH" + cd "$SOURCE/$ARCH" + + CFLAGS="-arch $ARCH" + if [ "$ARCH" = "i386" -o "$ARCH" = "x86_64" ] + then + PLATFORM="iPhoneSimulator" + CFLAGS="$CFLAGS -mios-simulator-version-min=$DEPLOYMENT_TARGET" + else + PLATFORM="iPhoneOS" + CFLAGS="$CFLAGS -mios-version-min=$DEPLOYMENT_TARGET -fembed-bitcode" + if [ "$ARCH" = "arm64" ] + then + EXPORT="GASPP_FIX_XCODE5=1" + fi + fi + + XCRUN_SDK=`echo $PLATFORM | tr '[:upper:]' '[:lower:]'` + CC="xcrun -sdk $XCRUN_SDK clang" + + # force "configure" to use "gas-preprocessor.pl" (FFmpeg 3.3) + if [ "$ARCH" = "arm64" ] + then + AS="gas-preprocessor.pl -arch aarch64 -- $CC" + else + AS="gas-preprocessor.pl -- $CC" + fi + + CXXFLAGS="$CFLAGS" + LDFLAGS="$CFLAGS" + SPEEX=$THIN/speex/$ARCH + + if [ -f "${SPEEX}/lib/libspeex.a" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-libspeex" + CFLAGS="$CFLAGS -I$SPEEX/include" + LDFLAGS="$LDFLAGS -L$SPEEX/lib -lspeex" + fi + + echo "configure $ARCH -> $SOURCE/ffmpeg-$FF_VERSION" + + TMPDIR=${TMPDIR/%\/} $SOURCE/ffmpeg-$FF_VERSION/configure \ + --target-os=darwin \ + --arch=$ARCH \ + --cc="$CC" \ + --as="$AS" \ + $CONFIGURE_FLAGS \ + --extra-cflags="$CFLAGS" \ + --extra-ldflags="$LDFLAGS" \ + --prefix="$FFMPEG/$ARCH" \ + || exit 1 + + make -j3 install $EXPORT || exit 1 + cd $CWD + done +fi + +if [ "$LIPO" ] +then + echo "building fat binaries..." + mkdir -p $FAT/lib + set - $ARCHS + CWD=`pwd` + cd $FFMPEG/$1/lib + for LIB in *.a + do + cd $CWD + echo lipo -create `find $FFMPEG -name $LIB` -output $FAT/lib/$LIB 1>&2 + lipo -create `find $FFMPEG -name $LIB` -output $FAT/lib/$LIB || exit 1 + done + + cd $CWD + cp -rf $FFMPEG/$1/include $FAT +fi + +echo Done diff --git a/libs/build-ogg.sh b/libs/build-ogg.sh new file mode 100755 index 0000000..416d54a --- /dev/null +++ b/libs/build-ogg.sh @@ -0,0 +1,104 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2014 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#---------- +# modify for your build tool + +FF_ALL_ARCHS_IOS6_SDK="armv7 armv7s i386" +FF_ALL_ARCHS_IOS7_SDK="armv7 armv7s arm64 i386 x86_64" +FF_ALL_ARCHS_IOS8_SDK="armv7 arm64 i386 x86_64" + +FF_ALL_ARCHS=$FF_ALL_ARCHS_IOS7_SDK + +#---------- +UNI_BUILD_ROOT=`pwd` +LIBS="$UNI_BUILD_ROOT/build/universal" +UNI_TMP="$UNI_BUILD_ROOT/tmp" +UNI_TMP_LLVM_VER_FILE="$UNI_TMP/llvm.ver.txt" +FF_TARGET=$1 +set -e + +#---------- +FF_LIBS="libogg" + +#---------- +echo_archs() { + echo "====================" + echo "[*] check xcode version" + echo "====================" + echo "FF_ALL_ARCHS = $FF_ALL_ARCHS" +} + +do_lipo () { + LIB_FILE=$1 + LIPO_FLAGS= + for ARCH in $FF_ALL_ARCHS + do + LIPO_FLAGS="$LIPO_FLAGS $UNI_BUILD_ROOT/build/thin/ogg/$ARCH/lib/$LIB_FILE" + done + + xcrun lipo -create $LIPO_FLAGS -output $LIBS/lib/$LIB_FILE + xcrun lipo -info $LIBS/lib/$LIB_FILE +} + +do_lipo_all () { + mkdir -p $LIBS/lib + echo "lipo archs: $FF_ALL_ARCHS" + for FF_LIB in $FF_LIBS + do + do_lipo "$FF_LIB.a"; + done + + cp -R $UNI_BUILD_ROOT/build/thin/ogg/armv7/include $LIBS/ +} + +#---------- +if [ "$FF_TARGET" = "armv7" -o "$FF_TARGET" = "armv7s" -o "$FF_TARGET" = "arm64" ]; then + echo_archs + sh tools/do-compile-ogg.sh $FF_TARGET +elif [ "$FF_TARGET" = "i386" -o "$FF_TARGET" = "x86_64" ]; then + echo_archs + sh tools/do-compile-ogg.sh $FF_TARGET +elif [ "$FF_TARGET" = "lipo" ]; then + echo_archs + do_lipo_all +elif [ "$FF_TARGET" = "all" ]; then + echo_archs + for ARCH in $FF_ALL_ARCHS + do + sh tools/do-compile-ogg.sh $ARCH + done + + do_lipo_all +elif [ "$FF_TARGET" = "check" ]; then + echo_archs +elif [ "$FF_TARGET" = "clean" ]; then + echo_archs + for ARCH in $FF_ALL_ARCHS + do + cd ogg/$ARCH && git clean -xdf && cd - + done +else + echo "Usage:" + echo " compile-ogg.sh armv7|arm64|i386|x86_64" + echo " compile-ogg.sh armv7s (obselete)" + echo " compile-ogg.sh lipo" + echo " compile-ogg.sh all" + echo " compile-ogg.sh clean" + echo " compile-ogg.sh check" + exit 1 +fi diff --git a/libs/build-speex.sh b/libs/build-speex.sh new file mode 100755 index 0000000..2ac5bfc --- /dev/null +++ b/libs/build-speex.sh @@ -0,0 +1,105 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2014 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#---------- +# modify for your build tool + +FF_ALL_ARCHS_IOS6_SDK="armv7 armv7s i386" +FF_ALL_ARCHS_IOS7_SDK="armv7 armv7s arm64 i386 x86_64" +FF_ALL_ARCHS_IOS8_SDK="armv7 arm64 i386 x86_64" + +FF_ALL_ARCHS=$FF_ALL_ARCHS_IOS7_SDK + +#---------- +UNI_BUILD_ROOT=`pwd` +LIBS="$UNI_BUILD_ROOT/build/universal" +UNI_TMP="$UNI_BUILD_ROOT/tmp" +UNI_TMP_LLVM_VER_FILE="$UNI_TMP/llvm.ver.txt" +FF_TARGET=$1 +set -e + +#---------- +FF_LIBS="libspeex" +OGG_LIBS="libogg" + +#---------- +echo_archs() { + echo "====================" + echo "[*] check xcode version" + echo "====================" + echo "FF_ALL_ARCHS = $FF_ALL_ARCHS" +} + +do_lipo () { + LIB_FILE=$1 + LIPO_FLAGS= + for ARCH in $FF_ALL_ARCHS + do + LIPO_FLAGS="$LIPO_FLAGS $UNI_BUILD_ROOT/build/thin/speex/$ARCH/lib/$LIB_FILE" + done + + xcrun lipo -create $LIPO_FLAGS -output $LIBS/lib/$LIB_FILE + xcrun lipo -info $LIBS/lib/$LIB_FILE +} + +do_lipo_all () { + mkdir -p $LIBS/lib + echo "lipo archs: $FF_ALL_ARCHS" + for FF_LIB in $FF_LIBS + do + do_lipo "$FF_LIB.a"; + done + + cp -R $UNI_BUILD_ROOT/build/thin/speex/armv7/include $LIBS +} + +#---------- +if [ "$FF_TARGET" = "armv7" -o "$FF_TARGET" = "armv7s" -o "$FF_TARGET" = "arm64" ]; then + echo_archs + sh tools/do-compile-speex.sh $FF_TARGET +elif [ "$FF_TARGET" = "i386" -o "$FF_TARGET" = "x86_64" ]; then + echo_archs + sh tools/do-compile-speex.sh $FF_TARGET +elif [ "$FF_TARGET" = "lipo" ]; then + echo_archs + do_lipo_all +elif [ "$FF_TARGET" = "all" ]; then + echo_archs + for ARCH in $FF_ALL_ARCHS + do + sh tools/do-compile-speex.sh $ARCH + done + + do_lipo_all +elif [ "$FF_TARGET" = "check" ]; then + echo_archs +elif [ "$FF_TARGET" = "clean" ]; then + echo_archs + for ARCH in $FF_ALL_ARCHS + do + cd speex/$ARCH && git clean -xdf && cd - + done +else + echo "Usage:" + echo " compile-speex.sh armv7|arm64|i386|x86_64" + echo " compile-speex.sh armv7s (obselete)" + echo " compile-speex.sh lipo" + echo " compile-speex.sh all" + echo " compile-speex.sh clean" + echo " compile-speex.sh check" + exit 1 +fi diff --git a/libs/build.sh b/libs/build.sh new file mode 100755 index 0000000..5efe93a --- /dev/null +++ b/libs/build.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +TARGETS=$1 +LIBS=`pwd`"/build/universal" + +function buildLib() { + if [ -d "${LIBS}/include/$1" ] && [ -f "${LIBS}/lib/lib$1.a" ] + then + echo "Already compiled $1." + else + sh pull-$1.sh "all" + sh build-$1.sh "all" + fi +} + +function buildFFmpeg() { + if [ -d "${LIBS}/include/libavcodec" ] && + [ -d "${LIBS}/include/libavdevice" ] && + [ -d "${LIBS}/include/libavfilter" ] && + [ -d "${LIBS}/include/libavformat" ] && + [ -d "${LIBS}/include/libavutil" ] && + [ -d "${LIBS}/include/libswresample" ] && + [ -d "${LIBS}/include/libswscale" ] && + [ -f "${LIBS}/lib/libavcodec.a" ] && + [ -f "${LIBS}/lib/libavdevice.a" ] && + [ -f "${LIBS}/lib/libavfilter.a" ] && + [ -f "${LIBS}/lib/libavformat.a" ] && + [ -f "${LIBS}/lib/libavutil.a" ] && + [ -f "${LIBS}/lib/libswresample.a" ] && + [ -f "${LIBS}/lib/libswscale.a" ] + then + echo "Already compiled ffmpeg." + else + sh build-ffmpeg.sh + fi +} + +function buildTargets() { + for TARGET in $1 + do + if [ $TARGET = "ffmpeg" ]; then + buildFFmpeg + elif [ $TARGET = "ogg" ]; then + buildLib $TARGET + else + buildLib $TARGET + fi + done +} + +if [ ! $TARGETS ] +then + buildTargets "speex ffmpeg" +else + buildTargets $TARGETS +fi diff --git a/libs/pull-ogg.sh b/libs/pull-ogg.sh new file mode 100755 index 0000000..6aef5bf --- /dev/null +++ b/libs/pull-ogg.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2015 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +IJK_OGG_UPSTREAM=https://github.com/xiph/ogg.git +IJK_OGG_FORK=https://github.com/xiph/ogg.git +IJK_OGG_COMMIT=v1.3.3 +IJK_OGG_LOCAL_REPO=build/src/ogg/ogg-${IJK_OGG_COMMIT} + +set -e +TOOLS=tools + +echo "== pull ogg base ==" +sh $TOOLS/pull-repo-base.sh $IJK_OGG_UPSTREAM $IJK_OGG_LOCAL_REPO + +function pull_fork() +{ + echo "== pull ogg fork $1 ==" + sh $TOOLS/pull-repo-ref.sh $IJK_OGG_FORK build/src/ogg/$1 ${IJK_OGG_LOCAL_REPO} + cd build/src/ogg/$1 + git checkout ${IJK_OGG_COMMIT} -B ffmpeg-build + cd - +} + +pull_fork "armv7" +pull_fork "armv7s" +pull_fork "arm64" +pull_fork "i386" +pull_fork "x86_64" diff --git a/libs/pull-speex.sh b/libs/pull-speex.sh new file mode 100755 index 0000000..a78137e --- /dev/null +++ b/libs/pull-speex.sh @@ -0,0 +1,42 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2015 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +IJK_SPEEX_UPSTREAM=https://github.com/xiph/speex +IJK_SPEEX_FORK=https://github.com/HatsuneMikuV/speex.git +IJK_SPEEX_COMMIT=Speex-1.2.0 +IJK_SPEEX_LOCAL_REPO=build/src/speex/${IJK_SPEEX_COMMIT} + +set -e +TOOLS=tools + +echo "== pull speex base ==" +sh $TOOLS/pull-repo-base.sh $IJK_SPEEX_UPSTREAM $IJK_SPEEX_LOCAL_REPO + +function pull_fork() +{ + echo "== pull speex fork $1 ==" + sh $TOOLS/pull-repo-ref.sh $IJK_SPEEX_FORK build/src/speex/$1 ${IJK_SPEEX_LOCAL_REPO} + cd build/src/speex/$1 + git checkout ${IJK_SPEEX_COMMIT} -B ffmpeg-build + cd - +} + +pull_fork "armv7" +pull_fork "armv7s" +pull_fork "arm64" +pull_fork "i386" +pull_fork "x86_64" diff --git a/libs/tools/do-compile-ogg.sh b/libs/tools/do-compile-ogg.sh new file mode 100755 index 0000000..bfb1531 --- /dev/null +++ b/libs/tools/do-compile-ogg.sh @@ -0,0 +1,186 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2014 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This script is based on projects below +# https://github.com/cxjwin/speex_libs/blob/master/libogg-1.3.2/build_ogg_ios.sh + +#-------------------- +echo "====================" +echo "[*] check host" +echo "====================" +set -e + + +FF_XCRUN_DEVELOPER=`xcode-select -print-path` +if [ ! -d "$FF_XCRUN_DEVELOPER" ]; then + echo "xcode path is not set correctly $FF_XCRUN_DEVELOPER does not exist (most likely because of xcode > 4.3)" + echo "run" + echo "sudo xcode-select -switch " + echo "for default installation:" + echo "sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer" + exit 1 +fi + +case $FF_XCRUN_DEVELOPER in + *\ * ) + echo "Your Xcode path contains whitespaces, which is not supported." + exit 1 + ;; +esac + + +#-------------------- +# include + + +#-------------------- +# common defines +FF_ARCH=$1 +if [ -z "$FF_ARCH" ]; then + echo "You must specific an architecture 'armv7, armv7s, arm64, i386, x86_64, ...'.\n" + exit 1 +fi + + +FF_BUILD_ROOT=`pwd` +FF_TAGET_OS="darwin" + + +# ogg build params +export COMMON_FF_CFG_FLAGS= + +OGG_CFG_FLAGS= +OGG_EXTRA_CFLAGS= +OGG_CFG_CPU= + +# i386, x86_64 +OGG_CFG_FLAGS_SIMULATOR="--host=arm" + +# armv7, armv7s, arm64 +OGG_CFG_FLAGS_ARM= +OGG_CFG_FLAGS_ARM="--host=arm-apple-darwin" + +echo "build_root: $FF_BUILD_ROOT" + +#-------------------- +echo "====================" +echo "[*] config arch $FF_ARCH" +echo "====================" + +FF_BUILD_NAME="unknown" +FF_XCRUN_PLATFORM="iPhoneOS" +FF_XCRUN_OSVERSION= +FF_GASPP_EXPORT= + +if [ "$FF_ARCH" = "i386" ]; then + FF_BUILD_NAME="ogg/i386" + FF_XCRUN_PLATFORM="iPhoneSimulator" + FF_XCRUN_OSVERSION="-mios-simulator-version-min=6.0 -fembed-bitcode" + OGG_CFG_FLAGS="$OGG_CFG_FLAGS_SIMULATOR $OGG_CFG_FLAGS" +elif [ "$FF_ARCH" = "x86_64" ]; then + FF_BUILD_NAME="ogg/x86_64" + FF_XCRUN_PLATFORM="iPhoneSimulator" + FF_XCRUN_OSVERSION="-mios-simulator-version-min=7.0 -fembed-bitcode" + OGG_CFG_FLAGS="$OGG_CFG_FLAGS_SIMULATOR $OGG_CFG_FLAGS" +elif [ "$FF_ARCH" = "armv7" ]; then + FF_BUILD_NAME="ogg/armv7" + FF_XCRUN_OSVERSION="-miphoneos-version-min=6.0 -fembed-bitcode" + OGG_CFG_FLAGS="$OGG_CFG_FLAGS_ARM $OGG_CFG_FLAGS" +elif [ "$FF_ARCH" = "armv7s" ]; then + FF_BUILD_NAME="ogg/armv7s" + OGG_CFG_CPU="--cpu=swift" + FF_XCRUN_OSVERSION="-miphoneos-version-min=6.0 -fembed-bitcode" + OGG_CFG_FLAGS="$OGG_CFG_FLAGS_ARM $OGG_CFG_FLAGS" +elif [ "$FF_ARCH" = "arm64" ]; then + FF_BUILD_NAME="ogg/arm64" + FF_XCRUN_OSVERSION="-miphoneos-version-min=7.0 -fembed-bitcode" + OGG_CFG_FLAGS="$OGG_CFG_FLAGS_ARM $OGG_CFG_FLAGS" + FF_GASPP_EXPORT="GASPP_FIX_XCODE5=1" +else + echo "unknown architecture $FF_ARCH"; + exit 1 +fi + +echo "build_name: $FF_BUILD_NAME" +echo "platform: $FF_XCRUN_PLATFORM" +echo "osversion: $FF_XCRUN_OSVERSION" + +#-------------------- +echo "====================" +echo "[*] make ios toolchain $FF_BUILD_NAME" +echo "====================" + + +FF_BUILD_SOURCE="$FF_BUILD_ROOT/build/src/$FF_BUILD_NAME" +FF_BUILD_PREFIX="$FF_BUILD_ROOT/build/thin/$FF_BUILD_NAME" + +mkdir -p $FF_BUILD_PREFIX + + +FF_XCRUN_SDK=`echo $FF_XCRUN_PLATFORM | tr '[:upper:]' '[:lower:]'` +FF_XCRUN_SDK_PLATFORM_PATH=`xcrun -sdk $FF_XCRUN_SDK --show-sdk-platform-path` +FF_XCRUN_SDK_PATH=`xcrun -sdk $FF_XCRUN_SDK --show-sdk-path` +FF_XCRUN_CC="xcrun -sdk $FF_XCRUN_SDK clang" + +export CROSS_TOP="$FF_XCRUN_SDK_PLATFORM_PATH/Developer" +export CROSS_SDK=`echo ${FF_XCRUN_SDK_PATH/#$CROSS_TOP\/SDKs\//}` +export BUILD_TOOL="$FF_XCRUN_DEVELOPER" +export CC="$FF_XCRUN_CC -arch $FF_ARCH $FF_XCRUN_OSVERSION" + +echo "build_source: $FF_BUILD_SOURCE" +echo "build_prefix: $FF_BUILD_PREFIX" +echo "CROSS_TOP: $CROSS_TOP" +echo "CROSS_SDK: $CROSS_SDK" +echo "BUILD_TOOL: $BUILD_TOOL" +echo "CC: $CC" + +#-------------------- +echo "\n--------------------" +echo "[*] configurate ogg" +echo "--------------------" + +OGG_CFG_FLAGS="$OGG_CFG_FLAGS --prefix=$FF_BUILD_PREFIX" + +# xcode configuration +export DEBUG_INFORMATION_FORMAT=dwarf-with-dsym + +function configure() { + cd $FF_BUILD_SOURCE + if [ -f "./Makefile" ]; then + echo 'reuse configure' + elif [ -f "./configure" ]; then + echo 'already run autogen.sh' + echo "config: $OGG_CFG_FLAGS" + ./Configure \ + $OGG_CFG_FLAGS + else + echo 'should run autogen.sh first' + echo "config: $OGG_CFG_FLAGS" + ./autogen.sh + configure + fi +} + +configure + +#-------------------- +echo "\n--------------------" +echo "[*] compile ogg" +echo "--------------------" +set +e +make +make install diff --git a/libs/tools/do-compile-speex.sh b/libs/tools/do-compile-speex.sh new file mode 100755 index 0000000..1ac2edc --- /dev/null +++ b/libs/tools/do-compile-speex.sh @@ -0,0 +1,203 @@ +#! /usr/bin/env bash +# +# Copyright (C) 2013-2014 Zhang Rui +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This script is based on projects below +# https://github.com/cxjwin/speex_libs/blob/master/speex-1.2rc2/build_speex_ios.sh + +#-------------------- +echo "====================" +echo "[*] check host" +echo "====================" +set -e + + +FF_XCRUN_DEVELOPER=`xcode-select -print-path` +if [ ! -d "$FF_XCRUN_DEVELOPER" ]; then + echo "xcode path is not set correctly $FF_XCRUN_DEVELOPER does not exist (most likely because of xcode > 4.3)" + echo "run" + echo "sudo xcode-select -switch " + echo "for default installation:" + echo "sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer" + exit 1 +fi + +case $FF_XCRUN_DEVELOPER in + *\ * ) + echo "Your Xcode path contains whitespaces, which is not supported." + exit 1 + ;; +esac + + +#-------------------- +# include + + +#-------------------- +# common defines +FF_ARCH=$1 +if [ -z "$FF_ARCH" ]; then + echo "You must specific an architecture 'armv7, armv7s, arm64, i386, x86_64, ...'.\n" + exit 1 +fi + + +FF_BUILD_ROOT=`pwd` +FF_TAGET_OS="darwin" + + +# speex build params +export COMMON_FF_CFG_FLAGS= + +SPEEX_CFG_FLAGS= +SPEEX_EXTRA_CFLAGS= +SPEEX_CFG_CPU= + +# i386, x86_64 +SPEEX_CFG_FLAGS_SIMULATOR="--host=arm" + +# armv7, armv7s, arm64 +SPEEX_CFG_FLAGS_ARM= +SPEEX_CFG_FLAGS_ARM="--host=arm-apple-darwin" + +echo "build_root: $FF_BUILD_ROOT" + +#-------------------- +echo "====================" +echo "[*] config arch $FF_ARCH" +echo "====================" + +FF_BUILD_NAME="unknown" +FF_XCRUN_PLATFORM="iPhoneOS" +FF_XCRUN_OSVERSION= +FF_GASPP_EXPORT= + +if [ "$FF_ARCH" = "i386" ]; then + FF_BUILD_NAME="speex/i386" + FF_BUILD_NAME_OGG=ogg/i386 + FF_XCRUN_PLATFORM="iPhoneSimulator" + FF_XCRUN_OSVERSION="-mios-simulator-version-min=6.0 -fembed-bitcode" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS_SIMULATOR $SPEEX_CFG_FLAGS" +elif [ "$FF_ARCH" = "x86_64" ]; then + FF_BUILD_NAME="speex/x86_64" + FF_BUILD_NAME_OGG=ogg/x86_64 + FF_XCRUN_PLATFORM="iPhoneSimulator" + FF_XCRUN_OSVERSION="-mios-simulator-version-min=7.0 -fembed-bitcode" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS_SIMULATOR $SPEEX_CFG_FLAGS" +elif [ "$FF_ARCH" = "armv7" ]; then + FF_BUILD_NAME="speex/armv7" + FF_BUILD_NAME_OGG=ogg/armv7 + FF_XCRUN_OSVERSION="-miphoneos-version-min=6.0 -fembed-bitcode" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS_ARM $SPEEX_CFG_FLAGS" +elif [ "$FF_ARCH" = "armv7s" ]; then + FF_BUILD_NAME="speex/armv7s" + FF_BUILD_NAME_OGG=ogg/armv7s + SPEEX_CFG_CPU="--cpu=swift" + FF_XCRUN_OSVERSION="-miphoneos-version-min=6.0 -fembed-bitcode" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS_ARM $SPEEX_CFG_FLAGS" +elif [ "$FF_ARCH" = "arm64" ]; then + FF_BUILD_NAME="speex/arm64" + FF_BUILD_NAME_OGG=ogg/arm64 + FF_XCRUN_OSVERSION="-miphoneos-version-min=7.0 -fembed-bitcode" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS_ARM $SPEEX_CFG_FLAGS" + FF_GASPP_EXPORT="GASPP_FIX_XCODE5=1" +else + echo "unknown architecture $FF_ARCH"; + exit 1 +fi + +echo "build_name: $FF_BUILD_NAME" +echo "platform: $FF_XCRUN_PLATFORM" +echo "osversion: $FF_XCRUN_OSVERSION" + +#-------------------- +echo "====================" +echo "[*] make ios toolchain $FF_BUILD_NAME" +echo "====================" + + +FF_BUILD_SOURCE="$FF_BUILD_ROOT/build/src/$FF_BUILD_NAME" +FF_BUILD_PREFIX="$FF_BUILD_ROOT/build/thin/$FF_BUILD_NAME" + +mkdir -p $FF_BUILD_PREFIX + + +FF_XCRUN_SDK=`echo $FF_XCRUN_PLATFORM | tr '[:upper:]' '[:lower:]'` +FF_XCRUN_SDK_PLATFORM_PATH=`xcrun -sdk $FF_XCRUN_SDK --show-sdk-platform-path` +FF_XCRUN_SDK_PATH=`xcrun -sdk $FF_XCRUN_SDK --show-sdk-path` +FF_XCRUN_CC="xcrun -sdk $FF_XCRUN_SDK clang" + +export CROSS_TOP="$FF_XCRUN_SDK_PLATFORM_PATH/Developer" +export CROSS_SDK=`echo ${FF_XCRUN_SDK_PATH/#$CROSS_TOP\/SDKs\//}` +export BUILD_TOOL="$FF_XCRUN_DEVELOPER" +export CC="$FF_XCRUN_CC -arch $FF_ARCH $FF_XCRUN_OSVERSION" + +echo "build_source: $FF_BUILD_SOURCE" +echo "build_prefix: $FF_BUILD_PREFIX" +echo "CROSS_TOP: $CROSS_TOP" +echo "CROSS_SDK: $CROSS_SDK" +echo "BUILD_TOOL: $BUILD_TOOL" +echo "CC: $CC" + +#-------------------- +echo "\n--------------------" +echo "[*] check OGG" +echo "----------------------" +FFMPEG_DEP_OGG=$FF_BUILD_ROOT/build/thin/$FF_BUILD_NAME_OGG +FFMPEG_DEP_OGG_LIB=$FF_BUILD_ROOT/build/thin/$FF_BUILD_NAME_OGG/lib +#-------------------- +# with ogg +if [ -f "${FFMPEG_DEP_OGG_LIB}/libogg.a" ]; then + echo "FFMPEG_DEP_OGG -> $FFMPEG_DEP_OGG" + SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS --with-ogg=${FFMPEG_DEP_OGG}" +fi + +#-------------------- +echo "\n--------------------" +echo "[*] configurate speex" +echo "--------------------" + +SPEEX_CFG_FLAGS="$SPEEX_CFG_FLAGS --prefix=$FF_BUILD_PREFIX" + +# xcode configuration +export DEBUG_INFORMATION_FORMAT=dwarf-with-dsym + +function configure() { + cd $FF_BUILD_SOURCE + if [ -f "./Makefile" ]; then + echo 'reuse configure' + elif [ -f "./configure" ]; then + echo 'already run autogen.sh' + echo "config: $SPEEX_CFG_FLAGS" + ./Configure \ + $SPEEX_CFG_FLAGS + else + echo 'should run autogen.sh first' + ./autogen.sh + configure + fi +} + +configure + +#-------------------- +echo "\n--------------------" +echo "[*] compile speex" +echo "--------------------" +set +e +make +make install diff --git a/libs/tools/pull-repo-base.sh b/libs/tools/pull-repo-base.sh new file mode 100755 index 0000000..59fb5c7 --- /dev/null +++ b/libs/tools/pull-repo-base.sh @@ -0,0 +1,15 @@ +#! /usr/bin/env bash + +REMOTE_REPO=$1 +LOCAL_WORKSPACE=$2 + + +if [ -z $REMOTE_REPO -o -z $LOCAL_WORKSPACE ]; then + echo "invalid call pull-repo.sh '$REMOTE_REPO' '$LOCAL_WORKSPACE'" +elif [ ! -d $LOCAL_WORKSPACE ]; then + git clone $REMOTE_REPO $LOCAL_WORKSPACE +else + cd $LOCAL_WORKSPACE + git fetch --all --tags + cd - +fi diff --git a/libs/tools/pull-repo-ref.sh b/libs/tools/pull-repo-ref.sh new file mode 100755 index 0000000..d2247ed --- /dev/null +++ b/libs/tools/pull-repo-ref.sh @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +REMOTE_REPO=$1 +LOCAL_WORKSPACE=$2 +REF_REPO=$3 + +if [ -z $1 -o -z $2 -o -z $3 ]; then + echo "invalid call pull-repo.sh '$1' '$2' '$3'" +elif [ ! -d $LOCAL_WORKSPACE ]; then + git clone --reference $REF_REPO $REMOTE_REPO $LOCAL_WORKSPACE + cd $LOCAL_WORKSPACE + git repack -a +else + cd $LOCAL_WORKSPACE + git fetch --all --tags + cd - +fi diff --git a/libs/tools/setup-as-commiter.sh b/libs/tools/setup-as-commiter.sh new file mode 100755 index 0000000..73b2bc8 --- /dev/null +++ b/libs/tools/setup-as-commiter.sh @@ -0,0 +1,7 @@ +#! /usr/bin/env bash + +git remote add gitcafe git@gitcafe.com:bbcallen/ijkplayer.git +git remote add oschina git@git.oschina.net:bbcallen/ijkplayer.git +git remote add csdn git@code.csdn.net:bbcallen/ijkplayer.git +git fetch --all + diff --git a/libs/tools/sync-mirrors.sh b/libs/tools/sync-mirrors.sh new file mode 100755 index 0000000..bde1174 --- /dev/null +++ b/libs/tools/sync-mirrors.sh @@ -0,0 +1,7 @@ +#! /usr/bin/env bash + +git push origin --all --follow-tags +git push gitcafe --all --follow-tags +git push oschina --all --follow-tags +git push csdn --all --follow-tags +