JLChen
2021-03-11 f61d0079e62f0aff83e15a5a3d584e7523f26321
2021-03-11 1.增加ON+集成丰林项目相关工程
103个文件已添加
8678 ■■■■■ 已修改文件
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.pbxproj 692 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/AppDelegate.h 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/AppDelegate.m 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/AppIcon.appiconset/Contents.json 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/Contents.json 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_answer.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_answer.imageset/ic_esvideo_on_answer.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_back.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_back.imageset/ic_esvideo_on_back.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_hangup.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_hangup.imageset/ic_esvideo_on_hangup.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_select.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_select.imageset/ic_esvideo_on_takephoto_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_unselect.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_unselect.imageset/ic_esvideo_on_takephoto_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_select.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_select.imageset/ic_esvideo_on_unlock_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_unselect.imageset/Contents.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_unselect.imageset/ic_esvideo_on_unlock_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/AudioSessionHelper.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/AudioSessionHelper.m 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Base.lproj/LaunchScreen.storyboard 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Base.lproj/Main.storyboard 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/Info.plist 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/CBToast.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/CBToast.m 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnIntercomViewController.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnIntercomViewController.m 1168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnMonitorViewController.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnMonitorViewController.m 929 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnUtlis.h 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESVideo.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESVideo.m 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/OpenViewController.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/OpenViewController.m 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/SceneDelegate.h 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/SceneDelegate.m 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/ViewController.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/ViewController.mm 556 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo/main.m 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemoTests/ESVideoPhoneSDKDemoTests.m 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemoTests/Info.plist 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemoUITests/ESVideoPhoneSDKDemoUITests.m 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemoUITests/Info.plist 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/ESVideoPhoneSDk 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/Headers/ESError.h 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/Headers/ESVideoPhone.h 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/Info.plist 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeDirectory 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements-1 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeResources 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeSignature 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_answer.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_back.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_hangup.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_takephoto_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_takephoto_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_unlock_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/ic_esvideo_on_unlock_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/图像 83@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/ON+可视对讲图标/矩形 1788@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.pbxproj 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.xcworkspace/contents.xcworkspacedata 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/xcshareddata/xcschemes/ESVideoOnXamarin.xcscheme 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/AudioSessionHelper.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/AudioSessionHelper.m 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/CBToast.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/CBToast.m 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnIntercomViewController.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnIntercomViewController.m 1168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnMonitorViewController.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnMonitorViewController.m 929 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnUtlis.h 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESVideo.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESVideo.m 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/ESVideoPhoneSDk 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Headers/ESError.h 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Headers/ESVideoPhone.h 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Info.plist 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeDirectory 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements-1 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeResources 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeSignature 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/ESVideoOnXamarin/README.md 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK.sln 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/ApiDefinition.cs 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Library/ESVideoPhoneSDk.a 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Library/libESVideoOnXamarin.a 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Properties/AssemblyInfo.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_answer.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_back.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_hangup.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_takephoto_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_takephoto_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_unlock_select.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_unlock_unselect.png 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK.csproj 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Structs.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.pbxproj
New file
@@ -0,0 +1,692 @@
// !$*UTF8*$!
{
    archiveVersion = 1;
    classes = {
    };
    objectVersion = 50;
    objects = {
/* Begin PBXBuildFile section */
        B924481124934E6A00F118F1 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B924481024934E6A00F118F1 /* AudioToolbox.framework */; };
        B95069EE25F9C63A00F6FD4F /* ESVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = B95069E625F9C63A00F6FD4F /* ESVideo.m */; };
        B95069EF25F9C63A00F6FD4F /* CBToast.m in Sources */ = {isa = PBXBuildFile; fileRef = B95069E825F9C63A00F6FD4F /* CBToast.m */; };
        B95069F025F9C63A00F6FD4F /* OpenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B95069EA25F9C63A00F6FD4F /* OpenViewController.m */; };
        B95069FC25F9C68D00F6FD4F /* ESOnIntercomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B95069F725F9C68D00F6FD4F /* ESOnIntercomViewController.m */; };
        B95069FD25F9C68D00F6FD4F /* ESOnMonitorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B95069F925F9C68D00F6FD4F /* ESOnMonitorViewController.m */; };
        C3385925233DAF06006940C3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C3385924233DAF06006940C3 /* AppDelegate.m */; };
        C3385928233DAF06006940C3 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C3385927233DAF06006940C3 /* SceneDelegate.m */; };
        C338592B233DAF06006940C3 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = C338592A233DAF06006940C3 /* ViewController.mm */; };
        C338592E233DAF06006940C3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C338592C233DAF06006940C3 /* Main.storyboard */; };
        C3385930233DAF12006940C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C338592F233DAF12006940C3 /* Assets.xcassets */; };
        C3385933233DAF12006940C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C3385931233DAF12006940C3 /* LaunchScreen.storyboard */; };
        C3385936233DAF12006940C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C3385935233DAF12006940C3 /* main.m */; };
        C3385940233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C338593F233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.m */; };
        C338594B233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = C338594A233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.m */; };
        C345D08B233EEE520079C82C /* AudioSessionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = C345D08A233EEE520079C82C /* AudioSessionHelper.m */; };
        C3EBF9E823A72BCE00B62DDD /* ESVideoPhoneSDk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3EBF9E723A72BCD00B62DDD /* ESVideoPhoneSDk.framework */; };
        C3F59377233E0717009F09E5 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F59376233E0717009F09E5 /* libbz2.tbd */; };
        C3F59379233E0723009F09E5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F59378233E0723009F09E5 /* libz.tbd */; };
        C3F5937B233E0745009F09E5 /* libiconv.2.4.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C3F5937A233E0744009F09E5 /* libiconv.2.4.0.tbd */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
        C338593C233DAF12006940C3 /* PBXContainerItemProxy */ = {
            isa = PBXContainerItemProxy;
            containerPortal = C3385918233DAF06006940C3 /* Project object */;
            proxyType = 1;
            remoteGlobalIDString = C338591F233DAF06006940C3;
            remoteInfo = ESVideoPhoneSDKDemo;
        };
        C3385947233DAF13006940C3 /* PBXContainerItemProxy */ = {
            isa = PBXContainerItemProxy;
            containerPortal = C3385918233DAF06006940C3 /* Project object */;
            proxyType = 1;
            remoteGlobalIDString = C338591F233DAF06006940C3;
            remoteInfo = ESVideoPhoneSDKDemo;
        };
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
        B924481024934E6A00F118F1 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
        B95069E125F9C63A00F6FD4F /* CBToast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBToast.h; sourceTree = "<group>"; };
        B95069E325F9C63A00F6FD4F /* ESVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESVideo.h; sourceTree = "<group>"; };
        B95069E525F9C63A00F6FD4F /* OpenViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenViewController.h; sourceTree = "<group>"; };
        B95069E625F9C63A00F6FD4F /* ESVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESVideo.m; sourceTree = "<group>"; };
        B95069E825F9C63A00F6FD4F /* CBToast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBToast.m; sourceTree = "<group>"; };
        B95069EA25F9C63A00F6FD4F /* OpenViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenViewController.m; sourceTree = "<group>"; };
        B95069F725F9C68D00F6FD4F /* ESOnIntercomViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESOnIntercomViewController.m; sourceTree = "<group>"; };
        B95069F825F9C68D00F6FD4F /* ESOnMonitorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnMonitorViewController.h; sourceTree = "<group>"; };
        B95069F925F9C68D00F6FD4F /* ESOnMonitorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESOnMonitorViewController.m; sourceTree = "<group>"; };
        B95069FA25F9C68D00F6FD4F /* ESOnIntercomViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnIntercomViewController.h; sourceTree = "<group>"; };
        B95069FB25F9C68D00F6FD4F /* ESOnUtlis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnUtlis.h; sourceTree = "<group>"; };
        C3385920233DAF06006940C3 /* HDLZigBeeESVideoDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HDLZigBeeESVideoDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
        C3385923233DAF06006940C3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
        C3385924233DAF06006940C3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
        C3385926233DAF06006940C3 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
        C3385927233DAF06006940C3 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
        C3385929233DAF06006940C3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
        C338592A233DAF06006940C3 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = "<group>"; };
        C338592D233DAF06006940C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
        C338592F233DAF12006940C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
        C3385932233DAF12006940C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
        C3385934233DAF12006940C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
        C3385935233DAF12006940C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
        C338593B233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ESVideoPhoneSDKDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
        C338593F233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ESVideoPhoneSDKDemoTests.m; sourceTree = "<group>"; };
        C3385941233DAF12006940C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
        C3385946233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ESVideoPhoneSDKDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
        C338594A233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ESVideoPhoneSDKDemoUITests.m; sourceTree = "<group>"; };
        C338594C233DAF13006940C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
        C345D089233EEE510079C82C /* AudioSessionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioSessionHelper.h; sourceTree = "<group>"; };
        C345D08A233EEE520079C82C /* AudioSessionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioSessionHelper.m; sourceTree = "<group>"; };
        C3EBF9E723A72BCD00B62DDD /* ESVideoPhoneSDk.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ESVideoPhoneSDk.framework; sourceTree = "<group>"; };
        C3F59376233E0717009F09E5 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; };
        C3F59378233E0723009F09E5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
        C3F5937A233E0744009F09E5 /* libiconv.2.4.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.2.4.0.tbd; path = usr/lib/libiconv.2.4.0.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
        C338591D233DAF06006940C3 /* Frameworks */ = {
            isa = PBXFrameworksBuildPhase;
            buildActionMask = 2147483647;
            files = (
                C3EBF9E823A72BCE00B62DDD /* ESVideoPhoneSDk.framework in Frameworks */,
                C3F5937B233E0745009F09E5 /* libiconv.2.4.0.tbd in Frameworks */,
                B924481124934E6A00F118F1 /* AudioToolbox.framework in Frameworks */,
                C3F59379233E0723009F09E5 /* libz.tbd in Frameworks */,
                C3F59377233E0717009F09E5 /* libbz2.tbd in Frameworks */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385938233DAF12006940C3 /* Frameworks */ = {
            isa = PBXFrameworksBuildPhase;
            buildActionMask = 2147483647;
            files = (
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385943233DAF13006940C3 /* Frameworks */ = {
            isa = PBXFrameworksBuildPhase;
            buildActionMask = 2147483647;
            files = (
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
        B95069E025F9C63A00F6FD4F /* On */ = {
            isa = PBXGroup;
            children = (
                B95069FB25F9C68D00F6FD4F /* ESOnUtlis.h */,
                B95069E125F9C63A00F6FD4F /* CBToast.h */,
                B95069E825F9C63A00F6FD4F /* CBToast.m */,
                B95069E325F9C63A00F6FD4F /* ESVideo.h */,
                B95069E625F9C63A00F6FD4F /* ESVideo.m */,
                B95069FA25F9C68D00F6FD4F /* ESOnIntercomViewController.h */,
                B95069F725F9C68D00F6FD4F /* ESOnIntercomViewController.m */,
                B95069F825F9C68D00F6FD4F /* ESOnMonitorViewController.h */,
                B95069F925F9C68D00F6FD4F /* ESOnMonitorViewController.m */,
                B95069E525F9C63A00F6FD4F /* OpenViewController.h */,
                B95069EA25F9C63A00F6FD4F /* OpenViewController.m */,
            );
            path = On;
            sourceTree = "<group>";
        };
        C3385917233DAF06006940C3 = {
            isa = PBXGroup;
            children = (
                C3385922233DAF06006940C3 /* ESVideoPhoneSDKDemo */,
                C338593E233DAF12006940C3 /* ESVideoPhoneSDKDemoTests */,
                C3385949233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests */,
                C3385921233DAF06006940C3 /* Products */,
                C3E915CA233DFE4C00F77354 /* Frameworks */,
            );
            sourceTree = "<group>";
        };
        C3385921233DAF06006940C3 /* Products */ = {
            isa = PBXGroup;
            children = (
                C3385920233DAF06006940C3 /* HDLZigBeeESVideoDemo.app */,
                C338593B233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.xctest */,
                C3385946233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.xctest */,
            );
            name = Products;
            sourceTree = "<group>";
        };
        C3385922233DAF06006940C3 /* ESVideoPhoneSDKDemo */ = {
            isa = PBXGroup;
            children = (
                B95069E025F9C63A00F6FD4F /* On */,
                C3385923233DAF06006940C3 /* AppDelegate.h */,
                C3385924233DAF06006940C3 /* AppDelegate.m */,
                C3385926233DAF06006940C3 /* SceneDelegate.h */,
                C3385927233DAF06006940C3 /* SceneDelegate.m */,
                C3385929233DAF06006940C3 /* ViewController.h */,
                C338592A233DAF06006940C3 /* ViewController.mm */,
                C345D089233EEE510079C82C /* AudioSessionHelper.h */,
                C345D08A233EEE520079C82C /* AudioSessionHelper.m */,
                C338592C233DAF06006940C3 /* Main.storyboard */,
                C338592F233DAF12006940C3 /* Assets.xcassets */,
                C3385931233DAF12006940C3 /* LaunchScreen.storyboard */,
                C3385934233DAF12006940C3 /* Info.plist */,
                C3385935233DAF12006940C3 /* main.m */,
            );
            path = ESVideoPhoneSDKDemo;
            sourceTree = "<group>";
        };
        C338593E233DAF12006940C3 /* ESVideoPhoneSDKDemoTests */ = {
            isa = PBXGroup;
            children = (
                C338593F233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.m */,
                C3385941233DAF12006940C3 /* Info.plist */,
            );
            path = ESVideoPhoneSDKDemoTests;
            sourceTree = "<group>";
        };
        C3385949233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests */ = {
            isa = PBXGroup;
            children = (
                C338594A233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.m */,
                C338594C233DAF13006940C3 /* Info.plist */,
            );
            path = ESVideoPhoneSDKDemoUITests;
            sourceTree = "<group>";
        };
        C3E915CA233DFE4C00F77354 /* Frameworks */ = {
            isa = PBXGroup;
            children = (
                B924481024934E6A00F118F1 /* AudioToolbox.framework */,
                C3EBF9E723A72BCD00B62DDD /* ESVideoPhoneSDk.framework */,
                C3F5937A233E0744009F09E5 /* libiconv.2.4.0.tbd */,
                C3F59378233E0723009F09E5 /* libz.tbd */,
                C3F59376233E0717009F09E5 /* libbz2.tbd */,
            );
            name = Frameworks;
            sourceTree = "<group>";
        };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
        C338591F233DAF06006940C3 /* ESVideoPhoneSDKDemo */ = {
            isa = PBXNativeTarget;
            buildConfigurationList = C338594F233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemo" */;
            buildPhases = (
                C338591C233DAF06006940C3 /* Sources */,
                C338591D233DAF06006940C3 /* Frameworks */,
                C338591E233DAF06006940C3 /* Resources */,
            );
            buildRules = (
            );
            dependencies = (
            );
            name = ESVideoPhoneSDKDemo;
            productName = ESVideoPhoneSDKDemo;
            productReference = C3385920233DAF06006940C3 /* HDLZigBeeESVideoDemo.app */;
            productType = "com.apple.product-type.application";
        };
        C338593A233DAF12006940C3 /* ESVideoPhoneSDKDemoTests */ = {
            isa = PBXNativeTarget;
            buildConfigurationList = C3385952233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemoTests" */;
            buildPhases = (
                C3385937233DAF12006940C3 /* Sources */,
                C3385938233DAF12006940C3 /* Frameworks */,
                C3385939233DAF12006940C3 /* Resources */,
            );
            buildRules = (
            );
            dependencies = (
                C338593D233DAF12006940C3 /* PBXTargetDependency */,
            );
            name = ESVideoPhoneSDKDemoTests;
            productName = ESVideoPhoneSDKDemoTests;
            productReference = C338593B233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.xctest */;
            productType = "com.apple.product-type.bundle.unit-test";
        };
        C3385945233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests */ = {
            isa = PBXNativeTarget;
            buildConfigurationList = C3385955233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemoUITests" */;
            buildPhases = (
                C3385942233DAF13006940C3 /* Sources */,
                C3385943233DAF13006940C3 /* Frameworks */,
                C3385944233DAF13006940C3 /* Resources */,
            );
            buildRules = (
            );
            dependencies = (
                C3385948233DAF13006940C3 /* PBXTargetDependency */,
            );
            name = ESVideoPhoneSDKDemoUITests;
            productName = ESVideoPhoneSDKDemoUITests;
            productReference = C3385946233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.xctest */;
            productType = "com.apple.product-type.bundle.ui-testing";
        };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
        C3385918233DAF06006940C3 /* Project object */ = {
            isa = PBXProject;
            attributes = {
                LastUpgradeCheck = 1100;
                ORGANIZATIONNAME = eTouchSky;
                TargetAttributes = {
                    C338591F233DAF06006940C3 = {
                        CreatedOnToolsVersion = 11.0;
                    };
                    C338593A233DAF12006940C3 = {
                        CreatedOnToolsVersion = 11.0;
                        TestTargetID = C338591F233DAF06006940C3;
                    };
                    C3385945233DAF13006940C3 = {
                        CreatedOnToolsVersion = 11.0;
                        TestTargetID = C338591F233DAF06006940C3;
                    };
                };
            };
            buildConfigurationList = C338591B233DAF06006940C3 /* Build configuration list for PBXProject "ESVideoPhoneSDKDemo" */;
            compatibilityVersion = "Xcode 9.3";
            developmentRegion = en;
            hasScannedForEncodings = 0;
            knownRegions = (
                en,
                Base,
            );
            mainGroup = C3385917233DAF06006940C3;
            productRefGroup = C3385921233DAF06006940C3 /* Products */;
            projectDirPath = "";
            projectRoot = "";
            targets = (
                C338591F233DAF06006940C3 /* ESVideoPhoneSDKDemo */,
                C338593A233DAF12006940C3 /* ESVideoPhoneSDKDemoTests */,
                C3385945233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests */,
            );
        };
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
        C338591E233DAF06006940C3 /* Resources */ = {
            isa = PBXResourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
                C3385933233DAF12006940C3 /* LaunchScreen.storyboard in Resources */,
                C3385930233DAF12006940C3 /* Assets.xcassets in Resources */,
                C338592E233DAF06006940C3 /* Main.storyboard in Resources */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385939233DAF12006940C3 /* Resources */ = {
            isa = PBXResourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385944233DAF13006940C3 /* Resources */ = {
            isa = PBXResourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
        C338591C233DAF06006940C3 /* Sources */ = {
            isa = PBXSourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
                C338592B233DAF06006940C3 /* ViewController.mm in Sources */,
                C3385925233DAF06006940C3 /* AppDelegate.m in Sources */,
                B95069EF25F9C63A00F6FD4F /* CBToast.m in Sources */,
                B95069FC25F9C68D00F6FD4F /* ESOnIntercomViewController.m in Sources */,
                B95069FD25F9C68D00F6FD4F /* ESOnMonitorViewController.m in Sources */,
                B95069EE25F9C63A00F6FD4F /* ESVideo.m in Sources */,
                C345D08B233EEE520079C82C /* AudioSessionHelper.m in Sources */,
                C3385936233DAF12006940C3 /* main.m in Sources */,
                B95069F025F9C63A00F6FD4F /* OpenViewController.m in Sources */,
                C3385928233DAF06006940C3 /* SceneDelegate.m in Sources */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385937233DAF12006940C3 /* Sources */ = {
            isa = PBXSourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
                C3385940233DAF12006940C3 /* ESVideoPhoneSDKDemoTests.m in Sources */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
        C3385942233DAF13006940C3 /* Sources */ = {
            isa = PBXSourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
                C338594B233DAF13006940C3 /* ESVideoPhoneSDKDemoUITests.m in Sources */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
        C338593D233DAF12006940C3 /* PBXTargetDependency */ = {
            isa = PBXTargetDependency;
            target = C338591F233DAF06006940C3 /* ESVideoPhoneSDKDemo */;
            targetProxy = C338593C233DAF12006940C3 /* PBXContainerItemProxy */;
        };
        C3385948233DAF13006940C3 /* PBXTargetDependency */ = {
            isa = PBXTargetDependency;
            target = C338591F233DAF06006940C3 /* ESVideoPhoneSDKDemo */;
            targetProxy = C3385947233DAF13006940C3 /* PBXContainerItemProxy */;
        };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
        C338592C233DAF06006940C3 /* Main.storyboard */ = {
            isa = PBXVariantGroup;
            children = (
                C338592D233DAF06006940C3 /* Base */,
            );
            name = Main.storyboard;
            sourceTree = "<group>";
        };
        C3385931233DAF12006940C3 /* LaunchScreen.storyboard */ = {
            isa = PBXVariantGroup;
            children = (
                C3385932233DAF12006940C3 /* Base */,
            );
            name = LaunchScreen.storyboard;
            sourceTree = "<group>";
        };
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
        C338594D233DAF13006940C3 /* 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;
                COPY_PHASE_STRIP = NO;
                DEBUG_INFORMATION_FORMAT = dwarf;
                ENABLE_BITCODE = NO;
                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 = 10.0;
                MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
                MTL_FAST_MATH = YES;
                ONLY_ACTIVE_ARCH = YES;
                OTHER_LDFLAGS = "-Wl,-all_load";
                SDKROOT = iphoneos;
                VALID_ARCHS = "arm64 arm64e";
            };
            name = Debug;
        };
        C338594E233DAF13006940C3 /* 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;
                COPY_PHASE_STRIP = NO;
                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                ENABLE_BITCODE = NO;
                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 = 10.0;
                MTL_ENABLE_DEBUG_INFO = NO;
                MTL_FAST_MATH = YES;
                OTHER_LDFLAGS = "-Wl,-all_load";
                SDKROOT = iphoneos;
                VALIDATE_PRODUCT = YES;
                VALID_ARCHS = "arm64 arm64e";
            };
            name = Release;
        };
        C3385950233DAF13006940C3 /* Debug */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                CODE_SIGN_IDENTITY = "iPhone Developer";
                CODE_SIGN_STYLE = Manual;
                DEVELOPMENT_TEAM = BVTA78PRYA;
                FRAMEWORK_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/ESVideoPhoneSDKDemo",
                    "$(PROJECT_DIR)",
                );
                INFOPLIST_FILE = ESVideoPhoneSDKDemo/Info.plist;
                IPHONEOS_DEPLOYMENT_TARGET = 8.0;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                );
                LIBRARY_SEARCH_PATHS = "$(inherited)";
                MARKETING_VERSION = 1.2;
                OTHER_LDFLAGS = "-Wl,-all_load";
                PRODUCT_BUNDLE_IDENTIFIER = com.hdl.ESVideoPhoneSDKDemo;
                PRODUCT_NAME = HDLZigBeeESVideoDemo;
                PROVISIONING_PROFILE_SPECIFIER = Test;
                TARGETED_DEVICE_FAMILY = 1;
            };
            name = Debug;
        };
        C3385951233DAF13006940C3 /* Release */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
                CODE_SIGN_IDENTITY = "iPhone Developer";
                CODE_SIGN_STYLE = Manual;
                DEVELOPMENT_TEAM = BVTA78PRYA;
                FRAMEWORK_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/ESVideoPhoneSDKDemo",
                    "$(PROJECT_DIR)",
                );
                INFOPLIST_FILE = ESVideoPhoneSDKDemo/Info.plist;
                IPHONEOS_DEPLOYMENT_TARGET = 8.0;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                );
                LIBRARY_SEARCH_PATHS = "$(inherited)";
                MARKETING_VERSION = 1.2;
                OTHER_LDFLAGS = "-Wl,-all_load";
                PRODUCT_BUNDLE_IDENTIFIER = com.hdl.ESVideoPhoneSDKDemo;
                PRODUCT_NAME = HDLZigBeeESVideoDemo;
                PROVISIONING_PROFILE_SPECIFIER = Test;
                TARGETED_DEVICE_FAMILY = 1;
            };
            name = Release;
        };
        C3385953233DAF13006940C3 /* Debug */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                BUNDLE_LOADER = "$(TEST_HOST)";
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 754E2P9CSA;
                INFOPLIST_FILE = ESVideoPhoneSDKDemoTests/Info.plist;
                IPHONEOS_DEPLOYMENT_TARGET = 13.0;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                    "@loader_path/Frameworks",
                );
                PRODUCT_BUNDLE_IDENTIFIER = eTouchSky.ESVideoPhoneSDKDemoTests;
                PRODUCT_NAME = "$(TARGET_NAME)";
                TARGETED_DEVICE_FAMILY = "1,2";
                TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ESVideoPhoneSDKDemo.app/ESVideoPhoneSDKDemo";
            };
            name = Debug;
        };
        C3385954233DAF13006940C3 /* Release */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                BUNDLE_LOADER = "$(TEST_HOST)";
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 754E2P9CSA;
                INFOPLIST_FILE = ESVideoPhoneSDKDemoTests/Info.plist;
                IPHONEOS_DEPLOYMENT_TARGET = 13.0;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                    "@loader_path/Frameworks",
                );
                PRODUCT_BUNDLE_IDENTIFIER = eTouchSky.ESVideoPhoneSDKDemoTests;
                PRODUCT_NAME = "$(TARGET_NAME)";
                TARGETED_DEVICE_FAMILY = "1,2";
                TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ESVideoPhoneSDKDemo.app/ESVideoPhoneSDKDemo";
            };
            name = Release;
        };
        C3385956233DAF13006940C3 /* Debug */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 754E2P9CSA;
                INFOPLIST_FILE = ESVideoPhoneSDKDemoUITests/Info.plist;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                    "@loader_path/Frameworks",
                );
                PRODUCT_BUNDLE_IDENTIFIER = eTouchSky.ESVideoPhoneSDKDemoUITests;
                PRODUCT_NAME = "$(TARGET_NAME)";
                TARGETED_DEVICE_FAMILY = "1,2";
                TEST_TARGET_NAME = ESVideoPhoneSDKDemo;
            };
            name = Debug;
        };
        C3385957233DAF13006940C3 /* Release */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 754E2P9CSA;
                INFOPLIST_FILE = ESVideoPhoneSDKDemoUITests/Info.plist;
                LD_RUNPATH_SEARCH_PATHS = (
                    "$(inherited)",
                    "@executable_path/Frameworks",
                    "@loader_path/Frameworks",
                );
                PRODUCT_BUNDLE_IDENTIFIER = eTouchSky.ESVideoPhoneSDKDemoUITests;
                PRODUCT_NAME = "$(TARGET_NAME)";
                TARGETED_DEVICE_FAMILY = "1,2";
                TEST_TARGET_NAME = ESVideoPhoneSDKDemo;
            };
            name = Release;
        };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
        C338591B233DAF06006940C3 /* Build configuration list for PBXProject "ESVideoPhoneSDKDemo" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                C338594D233DAF13006940C3 /* Debug */,
                C338594E233DAF13006940C3 /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
        C338594F233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemo" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                C3385950233DAF13006940C3 /* Debug */,
                C3385951233DAF13006940C3 /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
        C3385952233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemoTests" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                C3385953233DAF13006940C3 /* Debug */,
                C3385954233DAF13006940C3 /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
        C3385955233DAF13006940C3 /* Build configuration list for PBXNativeTarget "ESVideoPhoneSDKDemoUITests" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                C3385956233DAF13006940C3 /* Debug */,
                C3385957233DAF13006940C3 /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
/* End XCConfigurationList section */
    };
    rootObject = C3385918233DAF06006940C3 /* Project object */;
}
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
New file
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:/Users/jlchen/JLChen/ProjectsCode/HDLGit/ESVideoPhoneSDKProject/ESVideoPhoneSDKZigbee/ESVideoPhoneSDKDemo/HDL/ESVideo.h">
   </FileRef>
   <FileRef
      location = "group:/Users/jlchen/JLChen/ProjectsCode/HDLGit/ESVideoPhoneSDKProject/ESVideoPhoneSDKZigbee/ESVideoPhoneSDKDemo/HDL/ESVideo.m">
   </FileRef>
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>
On+/ESVideoOn/ESVideoPhoneSDKDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>IDEDidComputeMac32BitWarning</key>
    <true/>
</dict>
</plist>
On+/ESVideoOn/ESVideoPhoneSDKDemo/AppDelegate.h
New file
@@ -0,0 +1,15 @@
//
//  AppDelegate.h
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/AppDelegate.m
New file
@@ -0,0 +1,51 @@
//
//  AppDelegate.m
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import "AppDelegate.h"
#import "OpenViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    OpenViewController *mainVC = [[OpenViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:mainVC];
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
}
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options  API_AVAILABLE(ios(13.0)) API_AVAILABLE(ios(13.0)) API_AVAILABLE(ios(13.0)){
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions  API_AVAILABLE(ios(13.0)){
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/AppIcon.appiconset/Contents.json
New file
@@ -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"
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/Contents.json
New file
@@ -0,0 +1,6 @@
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_answer.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_answer.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_answer.imageset/ic_esvideo_on_answer.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_back.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_back.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_back.imageset/ic_esvideo_on_back.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_hangup.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_hangup.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_hangup.imageset/ic_esvideo_on_hangup.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_select.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_takephoto_select.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_select.imageset/ic_esvideo_on_takephoto_select.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_unselect.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_takephoto_unselect.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_takephoto_unselect.imageset/ic_esvideo_on_takephoto_unselect.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_select.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_unlock_select.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_select.imageset/ic_esvideo_on_unlock_select.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_unselect.imageset/Contents.json
New file
@@ -0,0 +1,21 @@
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "ic_esvideo_on_unlock_unselect.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}
On+/ESVideoOn/ESVideoPhoneSDKDemo/Assets.xcassets/ic_esvideo_on_unlock_unselect.imageset/ic_esvideo_on_unlock_unselect.png
On+/ESVideoOn/ESVideoPhoneSDKDemo/AudioSessionHelper.h
New file
@@ -0,0 +1,19 @@
//
//  AudioSessionHelper.h
//  VideoPhone
//
//  Created by eTouchSky on 2019/5/30.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface AudioSessionHelper : NSObject
- (void)setAudioSession;
-(NSString *)speaker:(BOOL)isSpeaking;
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/AudioSessionHelper.m
New file
@@ -0,0 +1,124 @@
//
//  AudioSessionHelper.m
//  VideoPhone
//
//  Created by eTouchSky on 2019/5/30.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import "AudioSessionHelper.h"
#import <AVFoundation/AVFoundation.h>
@interface AudioSessionHelper()
@property (nonatomic,assign) BOOL                  speaking;
@end
@implementation AudioSessionHelper
#pragma mark -- AVAudioSession的设置
-(void)setAudioSession{
    NSError *error = nil;
    //免提播放 æœ‰å›žå£°ï¼š ç½‘络引起回音(两个手机隔太近就会很大)。 æ‰‹æœºæœ¬èº«çš„设计有问题(录播),关闭降噪功能(关闭后基本没有了)
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    if (error) {
        NSLog(@"AVAudioSessionCategoryPlayAndRecord error:%@", error);
    }else{
        NSLog(@"听筒播放");
    }
    [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
    [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:0.05 error:&error];
    if (error) {
        NSLog(@"AVAudioSessionSetPreferredIOBufferDuration error:%@", error);
    }
    /**< ç çŽ‡è®¾ç½®ä¸æ­£ç¡®ä¼šå¯¼è‡´æ’­æ”¾å‡ºæ¥çš„å£°éŸ³å˜å°–æˆ–å˜å¹³ï¼Œæœ‰ä¸€ç‚¹æ‹–éŸ³æˆ–è€…æœ€åŽä¸€ç‚¹éŸ³å°‘äº†,8000的采样率设置16000的码率很好>*/
    [[AVAudioSession sharedInstance] setPreferredSampleRate:16000 error:&error];
    if (error) {
        NSLog(@"setPreferredSampleRate error:%@", error);
    }
}
//必须先setActive:NO  å†è®¾ç½®option  å†setActive:YES, å¦åˆ™æœ‰äº›ç³»ç»Ÿï¼Œæœ‰äº›ç•¸å½¢ï¼Œåœ¨å¬ç­’模式下不能采集
//这里打断点测试可能会报错:p2p终端,解码器终端。
-(NSString *)speaker:(BOOL)isSpeaking{
    NSError *error = nil;
    [[AVAudioSession sharedInstance] setActive:NO error:&error];
    if (error) {
        NSLog(@"setActive:NO error:%@", error);
    }
    if (isSpeaking == NO) {
        //听筒播放 æ•ˆæžœå¾ˆå¥½
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
        if (error) {
            NSLog(@"AVAudioSessionCategoryPlayAndRecord error:%@", error);
            return nil;
        }else{
            NSLog(@"听筒播放");
             [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
            if (error) {
                NSLog(@"setActive:YES error:%@", error);
            }
           [self LogAudioSessionInfo];
            return @"免提";
        }
    }else{
        //免提播放 æœ‰å›žå£°ï¼š ç½‘络引起回音(两个手机隔太近就会很大)。 æ‰‹æœºæœ¬èº«çš„设计有问题(录播),关闭降噪功能(关闭后基本没有了)
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
        if (error) {
            NSLog(@"AVAudioSessionCategoryOptionDefaultToSpeaker error:%@", error);
            return nil;
        }else{
            NSLog(@"免提播放");
             [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
            if (error) {
                NSLog(@"setActive:YES error:%@", error);
            }
            //[self LogAudioSessionInfo];
            return @"听筒";
        }
    }
}
-(void)LogAudioSessionInfo{
    AVAudioSessionCategory category = [[AVAudioSession sharedInstance] category];
    AVAudioSessionCategoryOptions option = [[AVAudioSession sharedInstance] categoryOptions];
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    NSArray* outputArray = [AVAudioSession sharedInstance].currentRoute.outputs;
    NSLog(@"%@", category);
    NSLog(@"%lu", (unsigned long)option);
    NSLog(@"%@", inputArray);
    NSLog(@"%@", outputArray);
}
/*
-(void)changeMicToHeadset{
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    for (AVAudioSessionPortDescription* desc in inputArray) {
        if ([desc.portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
            NSError* error;
            [[AVAudioSession sharedInstance] setPreferredInput:desc error:&error];
        }
    }
}
-(void)changeMicToBuiltIn{
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    for (AVAudioSessionPortDescription* desc in inputArray) {
        if ([desc.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
            NSError* error;
            [[AVAudioSession sharedInstance] setPreferredInput:desc error:&error];
        }
    }
}
*/
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/Base.lproj/LaunchScreen.storyboard
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="oCk-sZ-pj6"/>
                        <viewControllerLayoutGuide type="bottom" id="850-qB-SUI"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>
On+/ESVideoOn/ESVideoPhoneSDKDemo/Base.lproj/Main.storyboard
New file
@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Wzv-3T-9rg"/>
                        <viewControllerLayoutGuide type="bottom" id="COG-Nv-9Ga"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vJi-oZ-LSc">
                                <rect key="frame" x="40" y="792" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="30" id="2Pc-3k-dUO"/>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="cuo-Ky-5Dv"/>
                                </constraints>
                                <state key="normal" title="听筒"/>
                                <connections>
                                    <action selector="speaker:" destination="BYZ-38-t0r" eventType="touchUpInside" id="W7q-iX-a2t"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jPl-Vg-g3C">
                                <rect key="frame" x="120" y="792" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="30" id="3O0-xV-2Xd"/>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="XJm-Mv-gZS"/>
                                </constraints>
                                <state key="normal" title="开门"/>
                                <connections>
                                    <action selector="openDoor:" destination="BYZ-38-t0r" eventType="touchUpInside" id="QiK-iu-wu4"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mdf-GC-mIQ">
                                <rect key="frame" x="200" y="792" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="Sm2-me-6Rj"/>
                                    <constraint firstAttribute="height" constant="30" id="ln6-ba-aYV"/>
                                </constraints>
                                <state key="normal" title="截图"/>
                                <connections>
                                    <action selector="onSnap:" destination="BYZ-38-t0r" eventType="touchUpInside" id="dvX-n1-L3W"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="koH-BL-KZA">
                                <rect key="frame" x="40" y="742" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="JB7-ql-xzA"/>
                                </constraints>
                                <state key="normal" title="反呼"/>
                                <connections>
                                    <action selector="onReverseCall:" destination="BYZ-38-t0r" eventType="touchUpInside" id="R22-bQ-kpF"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WJI-4Z-VeA">
                                <rect key="frame" x="120" y="742" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="csc-sy-eEr"/>
                                    <constraint firstAttribute="height" constant="30" id="vza-fv-qdl"/>
                                </constraints>
                                <state key="normal" title="挂断"/>
                                <connections>
                                    <action selector="onHangup:" destination="BYZ-38-t0r" eventType="touchUpInside" id="mUK-dK-0eT"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LcR-MR-y9c">
                                <rect key="frame" x="200" y="742" width="60" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="7N3-EE-cFS"/>
                                    <constraint firstAttribute="height" constant="30" id="go1-vo-L2j"/>
                                </constraints>
                                <state key="normal" title="监控"/>
                                <connections>
                                    <action selector="onMonitor:" destination="BYZ-38-t0r" eventType="touchUpInside" id="RcS-6u-LBX"/>
                                </connections>
                            </button>
                            <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Vfn-FR-1Ub">
                                <rect key="frame" x="102" y="44" width="282" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="30" id="JFw-sW-vcm"/>
                                </constraints>
                                <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                <textInputTraits key="textInputTraits" returnKeyType="done"/>
                            </textField>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="门口机" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E2O-cv-ota">
                                <rect key="frame" x="40" y="44" width="52" height="30"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="30" id="VRD-h4-kIL"/>
                                </constraints>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                        </subviews>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstItem="Vfn-FR-1Ub" firstAttribute="leading" secondItem="E2O-cv-ota" secondAttribute="trailing" constant="10" id="0RX-sz-eS9"/>
                            <constraint firstItem="E2O-cv-ota" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="20" id="17C-bm-Akt"/>
                            <constraint firstItem="E2O-cv-ota" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="20" id="1qo-7b-cDh"/>
                            <constraint firstAttribute="trailing" secondItem="LcR-MR-y9c" secondAttribute="trailing" constant="154" id="Ac2-pr-p5A"/>
                            <constraint firstItem="vJi-oZ-LSc" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="20" id="AeG-dA-Pth"/>
                            <constraint firstItem="vJi-oZ-LSc" firstAttribute="top" secondItem="koH-BL-KZA" secondAttribute="bottom" constant="20" id="Bgv-gv-AxZ"/>
                            <constraint firstItem="jPl-Vg-g3C" firstAttribute="leading" secondItem="vJi-oZ-LSc" secondAttribute="trailing" constant="20" id="F7k-hc-PjM"/>
                            <constraint firstItem="koH-BL-KZA" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="20" id="GCM-h5-uZT"/>
                            <constraint firstItem="E2O-cv-ota" firstAttribute="top" secondItem="Wzv-3T-9rg" secondAttribute="bottom" id="HUm-Q4-lcX"/>
                            <constraint firstItem="Vfn-FR-1Ub" firstAttribute="top" secondItem="Wzv-3T-9rg" secondAttribute="bottom" id="Ifi-g7-5KM"/>
                            <constraint firstItem="WJI-4Z-VeA" firstAttribute="leading" secondItem="koH-BL-KZA" secondAttribute="trailing" constant="20" id="MZc-33-aQU"/>
                            <constraint firstAttribute="trailing" secondItem="mdf-GC-mIQ" secondAttribute="trailing" constant="154" id="Q2A-hp-SjO"/>
                            <constraint firstItem="LcR-MR-y9c" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="WJI-4Z-VeA" secondAttribute="trailing" symbolic="YES" id="Uor-VU-t3a"/>
                            <constraint firstItem="Vfn-FR-1Ub" firstAttribute="top" secondItem="Wzv-3T-9rg" secondAttribute="bottom" id="Wua-9B-iLw"/>
                            <constraint firstItem="koH-BL-KZA" firstAttribute="baseline" secondItem="WJI-4Z-VeA" secondAttribute="baseline" id="hB6-19-6BO"/>
                            <constraint firstItem="Vfn-FR-1Ub" firstAttribute="leading" secondItem="E2O-cv-ota" secondAttribute="trailing" constant="10" id="hY9-F7-oE8"/>
                            <constraint firstItem="mdf-GC-mIQ" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="jPl-Vg-g3C" secondAttribute="trailing" symbolic="YES" id="hiz-Om-gmC"/>
                            <constraint firstAttribute="trailingMargin" secondItem="Vfn-FR-1Ub" secondAttribute="trailing" constant="10" id="nnC-yz-Mbc"/>
                            <constraint firstItem="mdf-GC-mIQ" firstAttribute="top" secondItem="LcR-MR-y9c" secondAttribute="bottom" constant="20" id="nyj-zh-x4K"/>
                            <constraint firstItem="koH-BL-KZA" firstAttribute="baseline" secondItem="LcR-MR-y9c" secondAttribute="baseline" id="uhc-hz-Lg4"/>
                            <constraint firstItem="COG-Nv-9Ga" firstAttribute="top" secondItem="vJi-oZ-LSc" secondAttribute="bottom" constant="40" id="w71-ij-wzw"/>
                            <constraint firstItem="COG-Nv-9Ga" firstAttribute="top" secondItem="jPl-Vg-g3C" secondAttribute="bottom" constant="40" id="zXK-Mn-zfv"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="mCallOrAccept" destination="koH-BL-KZA" id="Ryl-wC-8vS"/>
                        <outlet property="monitorBtn" destination="LcR-MR-y9c" id="quQ-29-c3m"/>
                        <outlet property="speakerBtn" destination="vJi-oZ-LSc" id="gAu-k3-y1u"/>
                        <outlet property="uIDTextField" destination="Vfn-FR-1Ub" id="En1-hG-ZxX"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="132" y="104"/>
        </scene>
    </scenes>
</document>
On+/ESVideoOn/ESVideoPhoneSDKDemo/Info.plist
New file
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
    <key>CFBundleShortVersionString</key>
    <string>$(MARKETING_VERSION)</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSCameraUsageDescription</key>
    <string>"需要使用相机"</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>"需要使用麦克风"</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>"需要使用相册"</string>
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>Main</string>
                </dict>
            </array>
        </dict>
    </dict>
    <key>UILaunchStoryboardName</key>
    <string>Main</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>armv7</string>
    </array>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
</dict>
</plist>
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/CBToast.h
New file
@@ -0,0 +1,21 @@
//
//  CBToast.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/10.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface CBToast : NSObject
//显示消息-->default center
+ (void)showToastAction:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/CBToast.m
New file
@@ -0,0 +1,110 @@
//
//  CBToast.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/10.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import "CBToast.h"
@implementation CBToast
static UILabel *toastLabel = nil;
+ (UILabel *)currentToastLabel {
    @synchronized(self) {
        if (toastLabel == nil) {
            toastLabel = [[UILabel alloc] init];
            toastLabel.backgroundColor = [UIColor darkGrayColor];
            toastLabel.font = [UIFont systemFontOfSize:16];
            toastLabel.textColor = [UIColor whiteColor];
            toastLabel.numberOfLines = 0;
            toastLabel.textAlignment = NSTextAlignmentCenter;
            toastLabel.lineBreakMode = NSLineBreakByCharWrapping;
            toastLabel.layer.masksToBounds = YES;
            toastLabel.layer.cornerRadius = 5.0;
            toastLabel.alpha = 0;
        }
        return toastLabel;
    }
}
//默认显示消息-->center
+ (void)showToastAction:(NSString *)message
{
    [self showToast:message location:@"center" showTime:2.0];
}
//显示消息
+ (void)showToast:(NSString *)message location:(NSString *)aLocationStr showTime:(float)aShowTime
{
    if (!message) {
        message = @"";
    }
    if ([[NSThread currentThread] isMainThread]) {
        toastLabel = [self currentToastLabel];
        [toastLabel removeFromSuperview];
        UIWindow *window = [UIApplication sharedApplication].delegate.window;
        [window addSubview:toastLabel];
        CGFloat main_width = [[UIScreen mainScreen] bounds].size.width;
        CGFloat main_height = [[UIScreen mainScreen] bounds].size.height;
        CGFloat width = [self stringText:message font:16 isHeightFixed:YES fixedValue:40];
        CGFloat height = 0;
        if (width > main_width - 20) {
            width = main_width - 20;
            height = [self stringText:message font:16 isHeightFixed:NO fixedValue:width];
        }else{
            height = 40;
        }
        CGRect labFrame;
        if (aLocationStr && [aLocationStr isEqualToString:@"top"]) {
            labFrame = CGRectMake((main_width-width)/2, main_height*0.15, width, height);
        }else if (aLocationStr && [aLocationStr isEqualToString:@"bottom"]) {
            labFrame = CGRectMake((main_width-width)/2, main_height*0.85, width, height);
        }else{
            //default-->center
            labFrame = CGRectMake((main_width-width)/2, main_height*0.5, width, height);
        }
        toastLabel.frame = labFrame;
        toastLabel.text = message;
        toastLabel.alpha = 1;
        [UIView animateWithDuration:aShowTime animations:^{
            toastLabel.alpha = 0;
        } completion:^(BOOL finished) {
        }];
    }else{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self showToast:message location:aLocationStr showTime:aShowTime];
        });
        return;
    }
}
//根据字符串长度获取对应的宽度或者高度
+ (CGFloat)stringText:(NSString *)aText font:(CGFloat)aFont isHeightFixed:(BOOL)isHeightFixed fixedValue:(CGFloat)fixedValue
{
    CGSize size;
    if (isHeightFixed) {
        size = CGSizeMake(MAXFLOAT, fixedValue);
    } else {
        size = CGSizeMake(fixedValue, MAXFLOAT);
    }
    CGSize resultSize = CGSizeZero;
    if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 7.0) {
        //返回计算出的size
        resultSize = [aText boundingRectWithSize:size options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:aFont]} context:nil].size;
    }
    if (isHeightFixed) {
        return resultSize.width + 20; //增加左右20间隔
    } else {
        return resultSize.height + 20; //增加上下20间隔
    }
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnIntercomViewController.h
New file
@@ -0,0 +1,22 @@
//
//  ESOnIntercomViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnIntercomViewController : UIViewController
@property (nonatomic,strong) NSString               *mESVideoID;
@property (nonatomic,assign) int                    mESRoomID;//住户的房间ID
@property (nonatomic,strong) NSString               *roomName;
@property (nonatomic,strong) NSString               *deviceName;
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnIntercomViewController.m
New file
@@ -0,0 +1,1168 @@
//
//  ESOnIntercomViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import "ESOnIntercomViewController.h"
#import "ESOnUtlis.h"
#import <AVFoundation/AVFoundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
#import "AudioSessionHelper.h"
#import <Photos/Photos.h>
#import <AudioToolbox/AudioToolbox.h>
#import "ESVideo.h"
@interface ESOnIntercomViewController ()<ESVideoPhoneDelegate>
@property (nonatomic,strong) AudioSessionHelper    *sessionHelper;
@property (nonatomic,strong) ESVideoPhone          *es;
@property (nonatomic,assign) BOOL                  playing;
@property (nonatomic,assign) BOOL                  isInterrupt;
@property (nonatomic,assign) BOOL                  isSpeaking;
@property (nonatomic,strong) UIImage               *snapImage; //截图
@property (nonatomic, strong) UIButton *backButton;  //
@property (nonatomic, strong) UILabel *titleUILabel;  //
@property (nonatomic, strong) UIView *centerView;  //内容背景View
@property (nonatomic, strong) UIView *videoView;
@property (nonatomic, strong) UIView *unlockView;
@property (nonatomic, strong) UIButton *screenshotImgBtn;  //截图
@property (nonatomic, strong) UIButton *unlockImgBtn;  //开锁
@property (nonatomic, strong) UIButton *hangUpImgBtn;//挂断按钮
@property (nonatomic, strong) UIButton *hangUpTextBtn;
@property (nonatomic, strong) UIButton *answerImgBtn;//接听按钮
@property (nonatomic, strong) UIButton *answerTextBtn;
@property (nonatomic, strong) UIButton *calltimeBtn; //通话时间按钮
//定时器
@property (nonatomic,strong) dispatch_source_t countdownTimer;
@property (nonatomic,strong) dispatch_source_t openDoorTimer;
@property (nonatomic, assign) int openDoorTimeout;
@property (nonatomic, assign) int callTimeout;
@end
@implementation ESOnIntercomViewController{
    BOOL isAccessAudio;
    BOOL isAccessVideo;
    BOOL isBackGround;
    BOOL iSVideoNotDetermined;
    BOOL iSAudioNotDetermined;
    NSString * tipStr;
    NSString * okStr;
    NSString * saveToTheAlbumsStr;
    NSString * operationFailedStr;
    NSString * refuseStr;
    NSString * answerStr;
    NSString * unlockSuccessfullyStr;
    NSString * callingStr;
    NSString * hangUpStr;
    //    int openDoorTimeout;
    //全局变量
    SystemSoundID sound;
    CGFloat Height66;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.whiteColor;
    Height66 = GetRealWidth(66);
    [self initLlanguage];
    [self initTopBarView];
    [self initCentetView];
    [self initData];
    //    [self setAnswerBtnEnable:NO];
    [self initESVideo];
    //开始反呼
    [self StartReverseCall];
    [self ShowCalltimeBtn:callingStr];
    // Do any additional setup after loading the view.
}
-(void)initLlanguage{
    NSString *languageName = [[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0];
    // ç®€ä½“中文
    if ([languageName rangeOfString:@"zh-Hans"].location != NSNotFound) {
        tipStr = @"提示";
        okStr = @"确认";
        saveToTheAlbumsStr = @"已保存至手机相册.";
        operationFailedStr = @"操作失败";
        refuseStr = @"拒绝";
        answerStr = @"接听";
        hangUpStr = @"挂断";
        unlockSuccessfullyStr = @"开锁成功";
        callingStr = @"来电中...";
    }else{
        tipStr = @"Prompt";
        okStr = @"OK";
        saveToTheAlbumsStr = @"Saved to the albums.";
        operationFailedStr = @"Operation failed.";
        refuseStr = @"Refuse";
        answerStr = @"Answer";
        hangUpStr = @"Hang up";
        unlockSuccessfullyStr = @"Unlock successfully";
        callingStr = @"Incoming call";
    }
}
- (void)initTopBarView {
    UIView *TopView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, APP_TOP_BAR_HEIGHT)];
    TopView.backgroundColor = HEXCOLORA(0xF7F7F7,1.0);
    //    [TopView addSubview:self.backButton];
    [TopView addSubview:self.titleUILabel];
    [self.view addSubview:TopView];
    TopView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
    TopView.layer.shadowOffset = CGSizeMake(0,0.5);
    TopView.layer.shadowOpacity = 1;
    TopView.layer.shadowRadius = 0;
}
- (UIButton *)backButton{
    if (_backButton == nil) {
        _backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_STATUS_BAR_HEIGHT, 44, 44)];
        [_backButton setImage:[UIImage imageNamed:@"ic_esvideo_on_back"] forState:UIControlStateNormal];
        _backButton.imageEdgeInsets = UIEdgeInsetsMake(12,12,12,12);
        [_backButton.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    }
    return _backButton;
}
-(void)backAction{
    [self.navigationController popViewControllerAnimated:true];
    //    [self dismissViewControllerAnimated:YES completion:NULL];
}
-(UILabel *)titleUILabel{
    if (_titleUILabel == Nil) {
        _titleUILabel = [[UILabel alloc] initWithFrame:CGRectMake(90, APP_STATUS_BAR_HEIGHT, self.view.frame.size.width - 180, 44)];
        _titleUILabel.font = [UIFont fontWithName:APP_UIFont_BOLD size:17.0];
        _titleUILabel.textColor = HEXCOLORA(0X1B2D4D, 1.0);
        _titleUILabel.text = @"";
        _titleUILabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleUILabel;
}
- (void)initCentetView {
    [self.view addSubview:self.centerView];
    //    [self.centerView addSubview:self.collectButton];
    [self.centerView addSubview:self.videoView];
    [self.centerView addSubview:self.unlockView];
    [self.unlockView addSubview:self.screenshotImgBtn];
    [self.unlockView addSubview:self.unlockImgBtn];
    [self.centerView addSubview:self.hangUpImgBtn];
    [self.centerView addSubview:self.hangUpTextBtn];
    [self.centerView addSubview:self.answerImgBtn];
    [self.centerView addSubview:self.answerTextBtn];
    [self.centerView addSubview:self.calltimeBtn];
}
- (UIView *)centerView{
    if (_centerView == nil) {
        _centerView = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_TOP_BAR_HEIGHT, APP_SCREEN_WIDTH, APP_VISIBLE_HEIGHT)];
        _centerView.backgroundColor = UIColor.whiteColor;
    }
    return _centerView;
}
-(void)setRadiusWithView:(UIView *)mView{
    //顶部圆角
    UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
    if (@available(iOS 11.0, *)) {
        mView.layer.cornerRadius = 20;
        mView.layer.maskedCorners =  (CACornerMask)corners;
    }else{
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:mView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(20,20)];
        //创建 layer
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = mView.bounds;
        //赋值
        maskLayer.path = maskPath.CGPath;
        mView.layer.mask = maskLayer;
    }
}
-(UIView *)videoView{
    if (_videoView == Nil) {
        _videoView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, GetRealWidth(210))];
        _videoView.backgroundColor = UIColor.lightGrayColor;
        _videoView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
        _videoView.layer.shadowOffset = CGSizeMake(0,0.5);
        _videoView.layer.shadowOpacity = 1;
        _videoView.layer.shadowRadius = 0;
    }
    return _videoView;
}
- (UIView *)unlockView{
    if (_unlockView == nil) {
        _unlockView = [[UIView alloc] initWithFrame:CGRectMake(0, GetRealWidth(210), APP_SCREEN_WIDTH, GetRealWidth(72))];
        _unlockView.backgroundColor = HEXCOLORA(0x232323, 1.0);
        _unlockView.layer.backgroundColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0].CGColor;
        _unlockView.layer.shadowColor = [UIColor colorWithRed:204/255.0 green:204/255.0 blue:204/255.0 alpha:0.4].CGColor;
        _unlockView.layer.shadowOffset = CGSizeMake(0,0.5);
        _unlockView.layer.shadowOpacity = 1;
        _unlockView.layer.shadowRadius = 0;
    }
    return _unlockView;
}
- (UIButton *)screenshotImgBtn{
    if (_screenshotImgBtn == nil) {
        _screenshotImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(44), GetRealWidth(44))];
        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
        //        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateSelected];
        [_screenshotImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotImgBtn.adjustsImageWhenHighlighted = NO;
        _screenshotImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealWidth(38));
    }
    return _screenshotImgBtn;
}
-(void)screenshotDownAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateNormal];
}
-(void)screenshotAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
    //截图
    if(_es){
        [_es onSnap];
    }
}
- (UIButton *)unlockImgBtn{
    if (_unlockImgBtn == nil) {
        _unlockImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0,0, GetRealWidth(44), GetRealWidth(44))];
        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
        //        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateSelected];
        [_unlockImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_unlockImgBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_unlockImgBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockImgBtn.adjustsImageWhenHighlighted = NO;
        _unlockImgBtn.center = CGPointMake((APP_SCREEN_WIDTH/4) * 3, GetRealWidth(38));
    }
    return _unlockImgBtn;
}
-(void)unlockDownAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateNormal];
}
-(void)unlockAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
    //开锁
    if(_es){
        [_es openTheDoorWithRoomid:_mESRoomID];
    }
}
#pragma æŒ‚断和开锁
- (UIButton *)hangUpImgBtn{
    if (_hangUpImgBtn == nil) {
        _hangUpImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_hangUpImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_hangup"] forState:UIControlStateNormal];
        //        [_hangUpImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_hangup"] forState:UIControlStateSelected];
        [_hangUpImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_hangUpImgBtn addTarget:self action:@selector(hangUpAction) forControlEvents:UIControlEventTouchUpInside];
        _hangUpImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealHeight(452) + Height66/2);
    }
    return _hangUpImgBtn;
}
//挂断按钮事件
-(void)hangUpAction{
    [self backAction];
}
- (UIButton *)hangUpTextBtn{
    if (_hangUpTextBtn == nil) {
        _hangUpTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_hangUpTextBtn setTitle:@"拒绝" forState:UIControlStateNormal];
        _hangUpTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _hangUpTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_hangUpTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        [_hangUpTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_hangUpTextBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside];
        _hangUpTextBtn.center = CGPointMake(_hangUpImgBtn.center.x, GetRealHeight(530));
    }
    return _hangUpTextBtn;
}
- (UIButton *)answerImgBtn{
    if (_answerImgBtn == nil) {
        _answerImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_answerImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_answer"] forState:UIControlStateNormal];
        //        [_answerImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_answer"] forState:UIControlStateSelected];
        [_answerImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_answerImgBtn addTarget:self action:@selector(answerIAction) forControlEvents:UIControlEventTouchUpInside];
        _answerImgBtn.center = CGPointMake((APP_SCREEN_WIDTH/4)*3, GetRealHeight(452) + Height66/2);
    }
    return _answerImgBtn;
}
-(void)answerIAction{
    [self stopPlaySystemSound];
    if(_es){
        //        [self stopPlaySystemSound];
        [_es onAccept];
    }else{
        NSLog(@"ES初始化失败");
    }
    _answerImgBtn.hidden = YES;
    _answerTextBtn.hidden = YES;
    //挂断按钮移动中间
    _hangUpImgBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, _hangUpImgBtn.center.y);
    _hangUpTextBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, _hangUpTextBtn.center.y);
    [_hangUpTextBtn setTitle:hangUpStr forState:UIControlStateNormal];
    //开始计时
    _callTimeout = 0;
    [self startCountdown];
}
-(void)setAnswerBtnEnable:(BOOL)ISEnable{
    [_answerImgBtn setEnabled:ISEnable];
    [_answerTextBtn setEnabled:ISEnable];
}
- (UIButton *)answerTextBtn{
    if (_answerTextBtn == nil) {
        _answerTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_answerTextBtn setTitle:@"接听" forState:UIControlStateNormal];
        _answerTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        _answerTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        //        _unlockTextBtn.titleLabel.textColor = TextColor;
        [_answerTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        [_answerTextBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
        [_answerTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_answerTextBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside];
        _answerTextBtn.center = CGPointMake(_answerImgBtn.center.x, GetRealHeight(530));
    }
    return _answerTextBtn;
}
- (UIButton *)calltimeBtn{
    if (_calltimeBtn == nil) {
        _calltimeBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(40))];
        _calltimeBtn.backgroundColor = HEXCOLORA(0x000000, 0.6);
        [_calltimeBtn setTitle:@"00:00" forState:UIControlStateNormal];
        _calltimeBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:14.0];
        _calltimeBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        [_calltimeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        _calltimeBtn.hidden = YES;
        _calltimeBtn.layer.cornerRadius = 12;
        _calltimeBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, GetRealWidth(340));
    }
    return _calltimeBtn;
}
-(void)ShowTime:(int)nowTime {
    if(_calltimeBtn.hidden) _calltimeBtn.hidden = NO;
    NSString *timeStr = [self timeFormatted:nowTime];
    [self setCalltimeButtonText:timeStr isTime:YES];
}
-(void)ShowCalltimeBtn:(NSString*) mesStr  {
    if(_calltimeBtn.hidden) _calltimeBtn.hidden = NO;
    [self setCalltimeButtonText:mesStr isTime:NO];
}
/*
 æ ¹æ®æ–‡å­—调整按钮宽
 */
-(void)setCalltimeButtonText:(NSString*) mesStr isTime:(BOOL)isTime
{
    [_calltimeBtn setTitle:mesStr forState:UIControlStateNormal];
    if(isTime){
        _calltimeBtn.frame = CGRectMake(0, 0, GetRealWidth(80), GetRealWidth(30));
    }else{
        _calltimeBtn.frame = CGRectMake(0, 0, GetRealWidth(115), GetRealWidth(30));
    }
    _calltimeBtn.center = CGPointMake(APP_SCREEN_WIDTH/2, GetRealWidth(340));
}
- (NSString *)timeFormatted:(int)totalSeconds
{
    int seconds = totalSeconds % 60;
    int minutes = (totalSeconds / 60);
    return [NSString stringWithFormat:@"%02d:%02d", minutes, seconds];
}
- (int *)getTextWidth:(UIButton*) btn
{
    int textWidth = 0;
    //   Â  CGSize size = [btn.titleLabel.textsizeWithFont:[UIFontboldSystemFontOfSize:15]constrainedToSize:contentMaxSizes lineBreakMode:UILineBreakModeCharacterWrap];
    //    textWidth = (int)fontSize.Width;
    return textWidth;
}
/** å¼€å¯å€’计时 */
- (void)startCountdown {
    if (_callTimeout > 100) {
        return;
    }
    _callTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _countdownTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_countdownTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_countdownTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.callTimeout >= 100 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.countdownTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"超时");
                [weakSelf_AT backAction];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.callTimeout++;
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT ShowTime:weakSelf_AT.callTimeout];
            });
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_countdownTimer);
}
#pragma å¼€é”æˆåŠŸ
-(void)setOpenDoorSuccess{
    [self setUnlock:NO];
    _openDoorTimeout = 0;
    [self startOpenDoorCountdown];
    [self showUIAlertView:unlockSuccessfullyStr];
}
-(void)setUnlock:(BOOL)ISEnable{
    [_unlockImgBtn setEnabled:ISEnable];
}
/** å¼€å¯å€’计时 */
- (void)startOpenDoorCountdown {
    if (_openDoorTimeout > 20) {
        return;
    }
    _openDoorTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _openDoorTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_openDoorTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_openDoorTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.openDoorTimeout >= 20 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.openDoorTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT setUnlock:YES];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.openDoorTimeout++;
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_openDoorTimer);
}
#pragma SDK可视对讲 åŠŸèƒ½éƒ¨åˆ†
-(void)initESVideo{
    _es = ESVideo.shareInstance.es;
    //初始化中断,进入后台的tag
    _playing = NO;
    _isSpeaking = NO;
    self.isInterrupt = NO;
    isBackGround = NO;
    [self requestAccessForAVMediaType:AVMediaTypeAudio];
    if (isAccessAudio) {
        ImageCallback snapImageCallback = ^(UIImage *image){
            //block是在分线程中调用的,这里要放到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self->_snapImage = image;
                [self saveImageToPhotosAlbum:image];
            });
        };
        //门口机会有视频的长宽高,是固定的(暂时还不确定)
        //        _es = [[ESVideoPhone alloc]initESVideoPhoneWithFrame:CGRectMake(0, 57, APP_SCREEN_WIDTH, 211) delegate:self imagecallBack:snapImageCallback];
        if (_es) {
            //判断视频渲染是否初始化成功,如果失败会走ESVideoPhoneDelegate方法
            if (_es.showView) {
                ESVideo.shareInstance.snapImageCallback = snapImageCallback;
                _es.delegate = self;
                _es.showView.backgroundColor = [UIColor whiteColor];
                [self.videoView addSubview:_es.showView];
                _es.showView.hidden = YES;
            }
        }else{
            NSLog(@"ESVideoPhone åˆå§‹åŒ–失败");
            return;
        }
        // åˆå§‹åŒ–Audio采集Unit
        if(![_es initAudioCaptureSession]){
            return;
        }
    }else{
        //音频没有权限建议不要发起通话
        return;
    }
    //初始化视频采集Capture
//    [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    if (isAccessVideo) {
//        if(![_es initVideoCaptureSession]){
//            NSLog(@"VideoCaptureSession åˆå§‹åŒ–失败");
//        }
//    }
    //初始化AudioSession
    _sessionHelper = [[AudioSessionHelper alloc]init];
    [_sessionHelper setAudioSession];
    //添加进入后台,中断等通知
    [self addObservers];
}
-(void)initData{
    _titleUILabel.text = _deviceName;
    [_hangUpTextBtn setTitle:refuseStr forState:UIControlStateNormal];
    [_answerTextBtn setTitle:answerStr forState:UIControlStateNormal];
}
/**
 å¼€å§‹åå‘¼
 */
-(void)StartReverseCall{
    [self startPlaySystemSound];
    if(_es){
        NSString *param = [NSString stringWithFormat:@"address=%@,tag=mobile://123,",_mESVideoID];
        //NSLog(@"%@", param);
        [_es onReverseCall:param];
    }else{
        NSLog(@"ES初始化失败");
    }
}
////接听
//-(void)onAccept{
//    if(_es){
//        [_es onAccept];
//    }else{
//        NSLog(@"ES初始化失败");
//    }
//
//}
-(NSString *)getCurrentdateInterval
{
    NSDate *datenow = [NSDate date];
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)([datenow timeIntervalSince1970]*1000)];
    return timeSp;
}
-(void)showUIAlertView:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alertController animated:YES completion:nil];
    //    UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:tipStr message:mes delegate:self cancelButtonTitle:okStr otherButtonTitles:nil, nil];
    //    [alertView1 show];
}
-(void)showUIAlertViewWithBack:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [self backAction];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
    //这个方法请根据App的具体情况调用
    //在viewDidLoad中 è°ƒç”¨requestAccessForAVMediaType: æ˜¯ä¸ºäº†èŠ‚çº¦åˆå§‹åŒ–çš„æ—¶é—´
    //在viewDidAppear中调用requestAccessForAVMediaType: æ˜¯ä¸ºäº†å¼¹å‡ºæç¤ºæ‰“开权限的Alert
    //测试的时候发现如下情况:如果只把授权方法放到ViewDidAppear方法中处理,如果没有授权在初始化采集器时会失败。同样AlertView会因为View没有didLoad而导致present不出来
//    if (!isAccessVideo || !isAccessAudio) {
//        [self requestAccessForAVMedia];
//    }
//
    if (!isAccessAudio) {
        [self requestAccessForAVMedia];
    }
}
-(void)setIsInterrupt:(BOOL)isInterrupt{
    if (_es) {
        _es.isInterrupt = isInterrupt;
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self stopPlaySystemSound];
    //防止用户不按挂断,或者不等收到对方的挂断,点击返回按钮。
    if(_es){
        [_es onHangup];
        [_es onStopCapture];
        [_es stopTalk];
        _es.delegate = nil;
        ESVideo.shareInstance.snapImageCallback = nil;
    }
    //    [_es freeSubClass];
}
-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    //    [_es freeSubClass];
    if(_openDoorTimer){
        dispatch_source_cancel(_openDoorTimer);
        _openDoorTimer = nil; // OK
    }
    if(_countdownTimer){
        dispatch_source_cancel(_countdownTimer);
        _countdownTimer = nil; // OK
    }
    NSLog(@"==============dealloc");
}
#pragma mark ESVideoPhoneDelegate
//视频通话的状态代理事件,phoneEvent为返回的消息里面包含event状态与与event相关的数据
-(void)getPhoneEvent_UI:(NSString *)phoneEvent{
    NSLog(@"事件%@", phoneEvent);
    NSArray *strArray = [phoneEvent componentsSeparatedByString:@"\r\n"];
    NSArray *eventArray = [strArray.firstObject componentsSeparatedByString:@"="];
    NSString *phoneEventStr = eventArray.lastObject;
    if([phoneEventStr isEqual:@"EVT_Ringing"]){
        _es.showView.hidden = NO;
        //反呼成功 å…è®¸æŽ¥å¬  çœ‹éœ€æ±‚是否需要播放铃声和震动
        [self setAnswerBtnEnable:YES];
        //           [_mCallOrAccept setTitle:@"接听" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_StartStream"]){
    } else if([phoneEventStr isEqual:@"EVT_StopStream"]){
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_Connected"]){
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_HangUp"]){
        [self showUIAlertViewWithBack:@"已挂断"];
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_P2POnlineStatusChanged"]){
        //EVT_P2PStarted(p2p初始化OK,可以连接),EVT_P2POnlineStatusChangedonline=1
        //p2p初始化成功,手机端目前没有这个回调了
        //_mCallOrAccept.enabled = YES;
        //_monitorBtn.enabled = YES;
    }else if([phoneEventStr  isEqual:@"EVT_RECV_CUSTOM_DATA"]){
        //开门的结果从这里返回
        NSString *baseStr = [strArray[1] substringFromIndex:5];
        NSData *data = [[NSData alloc]initWithBase64EncodedString:baseStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSInteger status = [[dic valueForKey:@"status"]integerValue];
        if(status && status == 1){
            NSLog(@"开门成功");
            [self setOpenDoorSuccess];
        }else{
            NSLog(@"开门失败");
        }
    }
}
-(void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger)errortype{
    NSLog(@"错误%@", errorStr);
    //没有授权
    if (errortype == LMPVideoCaptureErrorNotAuthorized) {
        NSLog(@"错误%@", errorStr);
    }
}
#pragma mark AudioSession与Notifications处理
- (void) addObservers
{
    //    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name: UIKeyboardWillChangeFrameNotification  object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
    //isAccessVideo,如果AVCaptureSession没有new出来不会收到通知
    if (isAccessVideo) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:nil];
    }else{
        //object:为nil å¯èƒ½ä¸ä¼šè§¦å‘通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification object:[AVAudioSession
                                                                                                         sharedInstance]];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) sessionRuntimeError:(NSNotification*)notification
{
    NSError* error = notification.userInfo[AVCaptureSessionErrorKey];
    NSLog(@"Capture session runtime error: %@", error);
    // If media services were reset, and the last start succeeded, restart the session.
    if (error.code == AVErrorMediaServicesWereReset) {
        [_es onStopCapture];
        [_es startTalk];
    }
}
- (void)handleInterruption:(NSNotification *)notification
{
    NSUInteger interruptionType = [[[notification userInfo]
                                    objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
        if (isBackGround) {
            return;
        }
        [_es stopTalk];
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
        if (self.isInterrupt == NO) {
            return;
        }else{
            //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
            [self InterruptionEndedAVAudioSessionSetActiveYES];
        }
    }
}
//AVAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
- (void) sessionWasInterrupted:(NSNotification*)notification
{
    if (_playing == YES) {
        self.isInterrupt = YES;
        //AVCaptureSessionInterruptionReason
        if (@available(iOS 9.0, *)) {
            NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; //电话中断是1
            NSLog(@"Capture session was interrupted with reason %ld", (long)reason);
            //音频硬件暂时不可用而造成的中断,例如,电话或警报。
            if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
                reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient");
                //VAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
                //Audio Queue Services, I/O audio unit
                [_es onStopCapture];
                [_es stopTalk];
                /*
                 NSError *error = nil;
                 [[AVAudioSession sharedInstance] setActive:NO error:&error];
                 if (error) {
                 NSLog(@"sessionWasInterruptedSetActiveNO error:%@", error);
                 }
                 */
            }else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground){
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground");
                //如果是电话中断,不会走进入后台的通知,进入后台再切换到前台这里是不用处理的
                if (isBackGround) {
                    return;
                }
                [_es onStopCapture];
                [_es stopTalk];
            }
            //多个应用程序资源争用质量下降。只有当应用程序占据全屏时,会话才能运行。
            else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps");
                // Fade-in a label to inform the user that the camera is unavailable.
            }else if (@available(iOS 11.1, *)) {
                if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure){
                    NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure");
                }
            } else {
                // Fallback on earlier versions
            }
        } else {
            if (isBackGround) {
                return;
            }
            [_es onStopCapture];
            [_es stopTalk];
        }
    }
}
//这个通知可能会获取不到,
- (void) sessionInterruptionEnded:(NSNotification*)notification
{
    //    NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
    NSLog(@"Capture session interruption ended");
    if (self.isInterrupt == NO) {
        return;
    }else{
        //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
        [self InterruptionEndedAVAudioSessionSetActiveYES];
    }
}
-(void)InterruptionEndedAVAudioSessionSetActiveYES{
    if (isBackGround) {
        return;
    }
    if (self.isInterrupt == YES) {
        [_es onStartCapture];
        [_es startTalk];
        self.isInterrupt = NO;
    }
}
- (void)speaker:(UIButton *)sender {
    [_es stopTalk];
    NSString *result = nil;
    //听筒状态 æ’耳塞后拔掉后恢复到默认设置
    if (sender == nil) {
        result = [_sessionHelper speaker:NO];
    }else{
        if(!_isSpeaking){
            result = [_sessionHelper speaker:YES];
            _isSpeaking = YES;
        }else{
            result = [_sessionHelper speaker:NO];
            _isSpeaking = NO;
        }
    }
    if (result) {
        [sender setTitle:result forState:UIControlStateNormal];
        [_es startTalk];
    }
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            //免提状态下耳机插入没有采集,同意切换到默认状态
            NSLog(@"耳机插入");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"耳机拔出");
            //            if([[_speakerBtn titleForState:UIControlStateNormal] isEqualToString:@"免提"]){
            //                 [self speaker:nil];
            //            }else{
            //                [self speaker:_speakerBtn];
            //            }
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            //NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
/*
 éœ€è¦æ³¨æ„çš„æ˜¯ï¼Œæœ‰ä¸€ä¸ªä¸­æ–­å¼€å§‹æ¶ˆæ¯ä¸ä¸€å®šä¼šæœ‰ä¸€ä¸ªä¸­æ–­ç»“束消息,这就意味着中断结束的回调里的处理逻辑可能会没有被执行到。
 æ‰€ä»¥éœ€è¦å…³æ³¨å½“切到前台运行状态时,是不是需要重新激活你的 Audio Session。
 */
- (void)willEnterForeground:(NSNotification*)notification{
    NSLog(@"willEnterForeground");
    //初次启动会走这个通知(根页面),这时候是没有进入后台的
    if (isBackGround) {
        return;
    }
    [self InterruptionEndedAVAudioSessionSetActiveYES];
    // è¿™é‡Œæ˜¯è€ƒè™‘到用户没有授权,之后通过AlertAction跳转到设置页面授权后再回到APP时做的重新检测
    //跳转到设置页面,授权后返回页面,继续初始化采集器
    if(!isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
    }
//    if (isAccessAudio && isAccessVideo) {
//        return;
//    }
//    if (isAccessVideo && !isAccessAudio) {
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
//    }else if (!isAccessVideo && isAccessAudio){
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//
//    }else if (!isAccessVideo && !isAccessAudio){
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//    }
}
- (void)willEnterBackground:(NSNotification *)notification {
    isBackGround = YES;
}
//授权Alert
-(void)requestAccessForAVMedia{
    if (!isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
    }
//    if (!isAccessVideo) {
//        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    }
    if (!iSAudioNotDetermined && iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"相机没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(!iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风与相机授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }
}
-(void)requestAccessForAVMediaType:(AVMediaType)type{
    if (type == AVMediaTypeVideo) {
        isAccessVideo = YES;
        iSVideoNotDetermined = YES;
    }else{
        isAccessAudio = YES;
        iSAudioNotDetermined = YES;
    }
    switch ([AVCaptureDevice authorizationStatusForMediaType:type])
    {
        case AVAuthorizationStatusAuthorized:
        {
            break;
        }
        case AVAuthorizationStatusNotDetermined:
        {
            dispatch_suspend(dispatch_get_main_queue());
            [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) {
                if (!granted) {
                    if (type == AVMediaTypeVideo) {
                        self->isAccessVideo = NO;
                    }else{
                        self->isAccessAudio = NO;
                    }
                }
                dispatch_resume(dispatch_get_main_queue());
            }];
            break;
        }
        default:
        {
            if (type == AVMediaTypeVideo) {
                isAccessVideo = NO;
                iSVideoNotDetermined = NO;
            }else{
                isAccessAudio = NO;
                iSAudioNotDetermined = NO;
            }
            break;
        }
    }
}
-(void)creatAlertViewWith:(NSString *)title message:(NSString *) msg cancel:(NSString *)cancelMsg{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:cancelMsg style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma ä¿å­˜å›¾ç‰‡åˆ°ç›¸å†Œ
- (void)saveImageToPhotosAlbum:(UIImage *)savedImage
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined)
    {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if(status == PHAuthorizationStatusAuthorized)
            {
                UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    }
    else
    {
        if (status == PHAuthorizationStatusAuthorized)
        {
            UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}
// æŒ‡å®šå›žè°ƒæ–¹æ³•
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *message = nil;
    if (!error) {
        message = saveToTheAlbumsStr;
    }
    else
    {
        message = operationFailedStr;
    }
    [self showUIAlertView:message];
}
#pragma éœ‡åŠ¨å®žçŽ°è²Œä¼¼å’ŒSDK冲突 ä¸èƒ½å®žçŽ°éœ‡åŠ¨
//开始播放的时候调用
-(void)startPlaySystemSound{
    return;
    //    //震动的提示文件名放到资源目录下
    //    NSString *path = [[NSBundle mainBundle] pathForResource:@"ring" ofType:@"wav"];
    //    AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &sound);
    //分别注册铃声和震动完后的回调
    AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, NULL, NULL, vibrationCompleteCallback, NULL);
    //    AudioServicesAddSystemSoundCompletion(sound, NULL, NULL, soundCompleteCallback, NULL);
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);//开始震动
    //    AudioServicesPlaySystemSound(sound);//开始播放铃声
}
//手动停止播放的时候调用
- (void)stopPlaySystemSound{
    return;
    NSLog(@"stop PlaySystemSound");
    stopRingAndVibration();
}
//停止响铃和震动,移除回调并处理掉铃声和震动
void stopRingAndVibration() {
    AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate);
    //    AudioServicesRemoveSystemSoundCompletion(sound);
    AudioServicesDisposeSystemSoundID(kSystemSoundID_Vibrate);
    //    AudioServicesDisposeSystemSoundID(sound);
}
//震动完成回调,因为震动一下便会调用一次,这里延迟800ms再继续震动,和微信差不多,时间长短可自己控制。参数sound即为注册回调时传的第一个参数
void vibrationCompleteCallback(SystemSoundID sound,void * clientData) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(800 * NSEC_PER_MSEC)), dispatch_get_global_queue(0, 0), ^{
        AudioServicesPlaySystemSound(sound);
    });
}
////铃声播放完成回调,这种方法x播放的音频限制在30秒内,播放完直接响铃和震动
//void soundCompleteCallback(SystemSoundID sound,void * clientData) {
//
//    stopRingAndVibration();
//}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnMonitorViewController.h
New file
@@ -0,0 +1,23 @@
//
//  ESOnMonitorViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnMonitorViewController : UIViewController
@property (nonatomic,strong) NSString               *mESVideoID;
@property (nonatomic,assign) int                    mESRoomID;//住户的房间ID
@property (nonatomic,strong) NSString               *roomName;
@property (nonatomic,strong) NSString               *deviceName;
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnMonitorViewController.m
New file
@@ -0,0 +1,929 @@
//
//  ESOnMonitorViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import "ESOnMonitorViewController.h"
#import "ESOnUtlis.h"
#import <AVFoundation/AVFoundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
#import <Photos/Photos.h>
#import "AudioSessionHelper.h"
//#import "CBToast.h"
#import "ESVideo.h"
@interface ESOnMonitorViewController ()<ESVideoPhoneDelegate>
@property (nonatomic,strong) AudioSessionHelper    *sessionHelper;
@property (nonatomic,strong) ESVideoPhone          *es;
@property (nonatomic,assign) BOOL                  playing;
@property (nonatomic,assign) BOOL                  isInterrupt;
@property (nonatomic,assign) BOOL                  isSpeaking;
@property (nonatomic,strong) UIImage               *snapImage; //截图
@property (nonatomic, strong) UIButton *backButton;  //
@property (nonatomic, strong) UILabel *titleUILabel;  //
@property (nonatomic, strong) UIView *centerView;  //内容背景View
@property (nonatomic, strong) UIView *videoView;
@property (nonatomic, strong) UIButton *screenshotImgBtn;  //
@property (nonatomic, strong) UIButton *screenshotTextBtn;  //
@property (nonatomic, strong) UIButton *unlockImgBtn;  //
@property (nonatomic, strong) UIButton *unlockTextBtn;  //
@property (nonatomic,strong) dispatch_source_t openDoorTimer;
@property (nonatomic, assign) int openDoorTimeout;
@end
@implementation ESOnMonitorViewController
{
    BOOL isAccessAudio;
    BOOL isAccessVideo;
    BOOL isBackGround;
    BOOL iSVideoNotDetermined;
    BOOL iSAudioNotDetermined;
    NSString * tipStr;
    NSString * okStr;
    NSString * saveToTheAlbumsStr;
    NSString * operationFailedStr;
    NSString * screenshotStr;
    NSString * unlockStr;
    NSString * endOfMonitoringStr;
    NSString * unlockSuccessfullyStr;
    CGFloat Height66;
    //    int _openDoorTimeout;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.whiteColor;
    Height66 = GetRealWidth(66);
    [self initLlanguage];
    [self initTopBarView];
    [self initCentetView];
    [self initData];
    [self initESVideo];
    [self StartMonitoring];
    // Do any additional setup after loading the view.
}
-(void)initLlanguage{
    NSString *languageName = [[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0];
    // ç®€ä½“中文
    if ([languageName rangeOfString:@"zh-Hans"].location != NSNotFound) {
        tipStr = @"提示";
        okStr = @"确认";
        saveToTheAlbumsStr = @"已保存至手机相册.";
        operationFailedStr = @"操作失败";
        screenshotStr = @"截图";
        unlockStr = @"开锁";
        endOfMonitoringStr = @"监视结束";
        unlockSuccessfullyStr = @"开锁成功";
    }else{
        tipStr = @"Prompt";
        okStr = @"OK";
        saveToTheAlbumsStr = @"Saved to the albums.";
        operationFailedStr = @"Operation failed.";
        screenshotStr = @"Screenshot";
        unlockStr = @"Unlock";
        endOfMonitoringStr = @"End of monitoring";
        unlockSuccessfullyStr = @"Unlock successfully";
    }
}
//顶部View
- (void)initTopBarView {
    UIView *TopView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, APP_TOP_BAR_HEIGHT)];
    TopView.backgroundColor = HEXCOLORA(0xF7F7F7,1.0);
    [TopView addSubview:self.backButton];
    [TopView addSubview:self.titleUILabel];
//    [TopView addSubview:self.moreButton];
    [self.view addSubview:TopView];
    TopView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
    TopView.layer.shadowOffset = CGSizeMake(0,0.5);
    TopView.layer.shadowOpacity = 1;
    TopView.layer.shadowRadius = 0;
}
- (UIButton *)backButton{
    if (_backButton == nil) {
        _backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_STATUS_BAR_HEIGHT, 44, 44)];
        [_backButton setImage:[UIImage imageNamed:@"ic_esvideo_on_back"] forState:UIControlStateNormal];
        _backButton.imageEdgeInsets = UIEdgeInsetsMake(12,12,12,12);
        [_backButton.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    }
    return _backButton;
}
-(void)backAction{
    [self.navigationController popViewControllerAnimated:true];
    //    [self dismissViewControllerAnimated:YES completion:NULL];
}
-(UILabel *)titleUILabel{
    if (_titleUILabel == Nil) {
        _titleUILabel = [[UILabel alloc] initWithFrame:CGRectMake(90, APP_STATUS_BAR_HEIGHT, self.view.frame.size.width - 180, 44)];
        _titleUILabel.font = [UIFont fontWithName:APP_UIFont_BOLD size:17.0];
        _titleUILabel.textColor = HEXCOLORA(0X1B2D4D, 1.0);
        _titleUILabel.text = @"";
        _titleUILabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleUILabel;
}
- (void)initCentetView {
    [self.view addSubview:self.centerView];
    //    [self.centerView addSubview:self.collectButton];
//    [self.centerView addSubview:self.homeView];
    [self.centerView addSubview:self.videoView];
    [self.centerView addSubview:self.screenshotImgBtn];
    [self.centerView addSubview:self.screenshotTextBtn];
    [self.centerView addSubview:self.unlockImgBtn];
    [self.centerView addSubview:self.unlockTextBtn];
}
- (UIView *)centerView{
    if (_centerView == nil) {
        _centerView = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_TOP_BAR_HEIGHT, APP_SCREEN_WIDTH, APP_VISIBLE_HEIGHT)];
        _centerView.backgroundColor = UIColor.whiteColor;
//        [self setRadiusWithView:_centerView];
    }
    return _centerView;
}
-(void)setRadiusWithView:(UIView *)mView{
    //顶部圆角
    UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
    if (@available(iOS 11.0, *)) {
        mView.layer.cornerRadius = 20;
        mView.layer.maskedCorners =  (CACornerMask)corners;
    }else{
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:mView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(20,20)];
        //创建 layer
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = mView.bounds;
        //赋值
        maskLayer.path = maskPath.CGPath;
        mView.layer.mask = maskLayer;
    }
}
-(UIView *)videoView{
    if (_videoView == Nil) {
        _videoView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, GetRealWidth((210)))];
        _videoView.backgroundColor = UIColor.lightGrayColor;
        _videoView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
        _videoView.layer.shadowOffset = CGSizeMake(0,0.5);
        _videoView.layer.shadowOpacity = 1;
        _videoView.layer.shadowRadius = 0;
    }
    return _videoView;
}
#pragma æˆªå›¾å›¾æ ‡æŒ‰é’®
- (UIButton *)screenshotImgBtn{
    if (_screenshotImgBtn == nil) {
        _screenshotImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
        //        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateSelected];
        [_screenshotImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotImgBtn.adjustsImageWhenHighlighted = NO;
        _screenshotImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealWidth(276) + Height66/2);
    }
    return _screenshotImgBtn;
}
-(void)screenshotDownAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateNormal];
    [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateNormal];
}
-(void)screenshotAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
    [_screenshotTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
    //截图
    if(_es){
        [_es onSnap];
    }
}
#pragma æˆªå›¾æ–‡å­—按钮
- (UIButton *)screenshotTextBtn{
    if (_screenshotTextBtn == nil) {
        _screenshotTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_screenshotTextBtn setTitle:@"截图" forState:UIControlStateNormal];
        _screenshotTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _screenshotTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_screenshotTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        //        [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_screenshotTextBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotTextBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotTextBtn.adjustsImageWhenHighlighted = NO;
        _screenshotTextBtn.center = CGPointMake(_screenshotImgBtn.center.x, GetRealWidth(364));
    }
    return _screenshotTextBtn;
}
- (UIButton *)unlockImgBtn{
    if (_unlockImgBtn == nil) {
        _unlockImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
        //        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateSelected];
        [_unlockImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_unlockImgBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_unlockImgBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockImgBtn.adjustsImageWhenHighlighted = NO;
        _unlockImgBtn.center = CGPointMake((APP_SCREEN_WIDTH / 4) * 3, GetRealWidth(276) + Height66/2);
    }
    return _unlockImgBtn;
}
-(void)unlockDownAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateNormal];
    [_unlockTextBtn setTitleColor:TextSelectColor forState:UIControlStateNormal];
}
-(void)unlockAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
    [_unlockTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
    //开锁
    if(_es){
        [_es openTheDoorWithRoomid:_mESRoomID];
    }
}
- (UIButton *)unlockTextBtn{
    if (_unlockTextBtn == nil) {
        _unlockTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_unlockTextBtn setTitle:@"开锁" forState:UIControlStateNormal];
        _unlockTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        _unlockTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        //        _unlockTextBtn.titleLabel.textColor = TextColor;
        [_unlockTextBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
        [_unlockTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        //        [_unlockTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_unlockTextBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside];
        [_unlockTextBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockTextBtn.adjustsImageWhenHighlighted = NO;
        _unlockTextBtn.center = CGPointMake(_unlockImgBtn.center.x, GetRealWidth(364));
    }
    return _unlockTextBtn;
}
#pragma å¼€é”æˆåŠŸ
-(void)setOpenDoorSuccess{
    [self setUnlock:NO];
    _openDoorTimeout = 0;
    [self startOpenDoorCountdown];
    [self showUIAlertView:unlockSuccessfullyStr];
}
-(void)setUnlock:(BOOL)ISEnable{
    [_unlockImgBtn setEnabled:ISEnable];
    [_unlockTextBtn setEnabled:ISEnable];
}
/** å¼€å¯å€’计时 */
- (void)startOpenDoorCountdown {
    if (_openDoorTimeout > 20) {
        return;
    }
    _openDoorTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _openDoorTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_openDoorTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_openDoorTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.openDoorTimeout >= 20 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.openDoorTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT setUnlock:YES];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.openDoorTimeout++;
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_openDoorTimer);
}
#pragma SDK可视对讲 åŠŸèƒ½éƒ¨åˆ†
-(void)initESVideo{
    //初始化中断,进入后台的tag
    _es = ESVideo.shareInstance.es;
    _playing = NO;
    _isSpeaking = NO;
    self.isInterrupt = NO;
    isBackGround = NO;
    [self requestAccessForAVMediaType:AVMediaTypeAudio];
    if (isAccessAudio) {
        ImageCallback snapImageCallback = ^(UIImage *image){
            //block是在分线程中调用的,这里要放到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self->_snapImage = image;
                [self saveImageToPhotosAlbum:image];
            });
        };
        //门口机会有视频的长宽高,是固定的(暂时还不确定)
        if (_es) {
            //判断视频渲染是否初始化成功,如果失败会走ESVideoPhoneDelegate方法
            if (_es.showView) {
                _es.delegate = self;
                ESVideo.shareInstance.snapImageCallback = snapImageCallback;
                _es.showView.backgroundColor = [UIColor whiteColor];
                [self.videoView addSubview:_es.showView];
                _es.showView.hidden = YES;
            }
        }else{
            NSLog(@"ESVideoPhone åˆå§‹åŒ–失败");
            return;
        }
        // åˆå§‹åŒ–Audio采集Unit
        if(![_es initAudioCaptureSession]){
            return;
        }
    }else{
        //音频没有权限建议不要发起通话
        return;
    }
    //初始化视频采集Capture
//    [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    if (isAccessVideo) {
//        if(![_es initVideoCaptureSession]){
//            NSLog(@"VideoCaptureSession åˆå§‹åŒ–失败");
//        }
//    }
    //初始化AudioSession
    _sessionHelper = [[AudioSessionHelper alloc]init];
    [_sessionHelper setAudioSession];
    //添加进入后台,中断等通知
    [self addObservers];
}
-(void)initData{
//    _topUILabel.text = _deviceName;
//    _homeUILabel.text = _roomName;
    [_screenshotTextBtn setTitle:screenshotStr forState:UIControlStateNormal];
    [_unlockTextBtn setTitle:unlockStr forState:UIControlStateNormal];
    _titleUILabel.text = _deviceName;
//    [_collectButton setSelected:_isCollect];
}
/**
 å¼€å§‹ç›‘控
 */
-(void)StartMonitoring{
    if(_es){
        [_es onMonitor:_mESVideoID];
    }else{
        NSLog(@"ES初始化失败");
    }
}
-(NSString *)getCurrentdateInterval
{
    NSDate *datenow = [NSDate date];
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)([datenow timeIntervalSince1970]*1000)];
    return timeSp;
}
-(void)showUIAlertView:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alertController animated:YES completion:nil];
}
-(void)showUIAlertViewWithBack:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [self backAction];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
    //这个方法请根据App的具体情况调用
    //在viewDidLoad中 è°ƒç”¨requestAccessForAVMediaType: æ˜¯ä¸ºäº†èŠ‚çº¦åˆå§‹åŒ–çš„æ—¶é—´
    //在viewDidAppear中调用requestAccessForAVMediaType: æ˜¯ä¸ºäº†å¼¹å‡ºæç¤ºæ‰“开权限的Alert
    //测试的时候发现如下情况:如果只把授权方法放到ViewDidAppear方法中处理,如果没有授权在初始化采集器时会失败。同样AlertView会因为View没有didLoad而导致present不出来
//    if (!isAccessVideo || !isAccessAudio) {
//        [self requestAccessForAVMedia];
//    }
    if (!isAccessAudio) {
        [self requestAccessForAVMedia];
    }
}
-(void)setIsInterrupt:(BOOL)isInterrupt{
    if (_es) {
        _es.isInterrupt = isInterrupt;
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    //防止用户不按挂断,或者不等收到对方的挂断,点击返回按钮。
    if(_es){
        [_es onHangup];
        [_es onStopCapture];
        [_es stopTalk];
        _es.delegate = nil;
        ESVideo.shareInstance.snapImageCallback = nil;
        //        _es.showView.removeFromSuperview;
    }
    //    [_es freeSubClass];
}
-(void)dealloc{
    NSLog(@"==============dealloc  1");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    NSLog(@"==============dealloc  2");
    //    [_es freeSubClass];
    //     NSLog(@"==============dealloc  3");
    if(_openDoorTimer){
        dispatch_source_cancel(_openDoorTimer);
        _openDoorTimer = nil; // OK
    }
    NSLog(@"==============dealloc 3");
}
#pragma mark ESVideoPhoneDelegate
//视频通话的状态代理事件,phoneEvent为返回的消息里面包含event状态与与event相关的数据
-(void)getPhoneEvent_UI:(NSString *)phoneEvent{
    NSLog(@"==============ESVideoPhoneDelegate事件%@", phoneEvent);
    NSArray *strArray = [phoneEvent componentsSeparatedByString:@"\r\n"];
    NSArray *eventArray = [strArray.firstObject componentsSeparatedByString:@"="];
    NSString *phoneEventStr = eventArray.lastObject;
    if([phoneEventStr isEqual:@"EVT_Ringing"]){
        //           [_mCallOrAccept setTitle:@"接听" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_StartStream"]){
    } else if([phoneEventStr isEqual:@"EVT_StopStream"]){
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_Connected"]){
        [self speaker:nil];
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_MonitorConnected"]){
        _es.showView.hidden = NO;
        [self speaker:nil];
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_HangUp"]){
        //        [CBToast showToastAction:endOfMonitoringStr];
        [self showUIAlertViewWithBack:endOfMonitoringStr];
        //        [self backAction];
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_P2POnlineStatusChanged"]){
        //EVT_P2PStarted(p2p初始化OK,可以连接),EVT_P2POnlineStatusChangedonline=1
        //p2p初始化成功,手机端目前没有这个回调了
        //_mCallOrAccept.enabled = YES;
        //_monitorBtn.enabled = YES;
    }else if([phoneEventStr  isEqual:@"EVT_RECV_CUSTOM_DATA"]){
        //开门的结果从这里返回
        NSString *baseStr = [strArray[1] substringFromIndex:5];
        NSData *data = [[NSData alloc]initWithBase64EncodedString:baseStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSInteger status = [[dic valueForKey:@"status"]integerValue];
        if(status && status == 1){
            NSLog(@"开门成功");
            [self setOpenDoorSuccess];
        }else{
            NSLog(@"开门失败");
        }
    }
}
-(void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger)errortype{
    NSLog(@"==============ErrorForESVideo错误%@", errorStr);
    //没有授权
    if (errortype == LMPVideoCaptureErrorNotAuthorized) {
        NSLog(@"错误%@", errorStr);
    }
}
#pragma mark AudioSession与Notifications处理
- (void) addObservers
{
    //    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name: UIKeyboardWillChangeFrameNotification  object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
    //isAccessVideo,如果AVCaptureSession没有new出来不会收到通知
    if (isAccessVideo) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:nil];
    }else{
        //object:为nil å¯èƒ½ä¸ä¼šè§¦å‘通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification object:[AVAudioSession
                                                                                                         sharedInstance]];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) sessionRuntimeError:(NSNotification*)notification
{
    NSError* error = notification.userInfo[AVCaptureSessionErrorKey];
    NSLog(@"Capture session runtime error: %@", error);
    // If media services were reset, and the last start succeeded, restart the session.
    if (error.code == AVErrorMediaServicesWereReset) {
        [_es onStopCapture];
        [_es startTalk];
    }
}
- (void)handleInterruption:(NSNotification *)notification
{
    NSUInteger interruptionType = [[[notification userInfo]
                                    objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
        if (isBackGround) {
            return;
        }
        [_es stopTalk];
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
        if (self.isInterrupt == NO) {
            return;
        }else{
            //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
            [self InterruptionEndedAVAudioSessionSetActiveYES];
        }
    }
}
//AVAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
- (void) sessionWasInterrupted:(NSNotification*)notification
{
    if (_playing == YES) {
        self.isInterrupt = YES;
        //AVCaptureSessionInterruptionReason
        if (@available(iOS 9.0, *)) {
            NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; //电话中断是1
            NSLog(@"Capture session was interrupted with reason %ld", (long)reason);
            //音频硬件暂时不可用而造成的中断,例如,电话或警报。
            if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
                reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient");
                //VAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
                //Audio Queue Services, I/O audio unit
                [_es onStopCapture];
                [_es stopTalk];
                /*
                 NSError *error = nil;
                 [[AVAudioSession sharedInstance] setActive:NO error:&error];
                 if (error) {
                 NSLog(@"sessionWasInterruptedSetActiveNO error:%@", error);
                 }
                 */
            }else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground){
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground");
                //如果是电话中断,不会走进入后台的通知,进入后台再切换到前台这里是不用处理的
                if (isBackGround) {
                    return;
                }
                [_es onStopCapture];
                [_es stopTalk];
            }
            //多个应用程序资源争用质量下降。只有当应用程序占据全屏时,会话才能运行。
            else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps");
                // Fade-in a label to inform the user that the camera is unavailable.
            }else if (@available(iOS 11.1, *)) {
                if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure){
                    NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure");
                }
            } else {
                // Fallback on earlier versions
            }
        } else {
            if (isBackGround) {
                return;
            }
            [_es onStopCapture];
            [_es stopTalk];
        }
    }
}
//这个通知可能会获取不到,
- (void) sessionInterruptionEnded:(NSNotification*)notification
{
    //    NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
    NSLog(@"Capture session interruption ended");
    if (self.isInterrupt == NO) {
        return;
    }else{
        //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
        [self InterruptionEndedAVAudioSessionSetActiveYES];
    }
}
-(void)InterruptionEndedAVAudioSessionSetActiveYES{
    if (isBackGround) {
        return;
    }
    if (self.isInterrupt == YES) {
        [_es onStartCapture];
        [_es startTalk];
        self.isInterrupt = NO;
    }
}
- (void)speaker:(UIButton *)sender {
    [_es stopTalk];
    NSString *result = nil;
    //    //听筒状态 æ’耳塞后拔掉后恢复到默认设置
    //    if (sender == nil) {
    //        result = [_sessionHelper speaker:NO];
    //    }else{
    //        if(!_isSpeaking){
    //            result = [_sessionHelper speaker:YES];
    //            _isSpeaking = YES;
    //        }else{
    //            result = [_sessionHelper speaker:NO];
    //            _isSpeaking = NO;
    //        }
    //    }
    //    if (result) {
    //        [sender setTitle:result forState:UIControlStateNormal];
    //        [_es startTalk];
    //    }
    if(!_isSpeaking){
        result = [_sessionHelper speaker:YES];
        _isSpeaking = YES;
    }else{
        result = [_sessionHelper speaker:NO];
        _isSpeaking = NO;
    }
    if (result) {
        //          [sender setTitle:result forState:UIControlStateNormal];
        [_es startTalk];
    }
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            //免提状态下耳机插入没有采集,同意切换到默认状态
            NSLog(@"耳机插入");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"耳机拔出");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            //NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
/*
 éœ€è¦æ³¨æ„çš„æ˜¯ï¼Œæœ‰ä¸€ä¸ªä¸­æ–­å¼€å§‹æ¶ˆæ¯ä¸ä¸€å®šä¼šæœ‰ä¸€ä¸ªä¸­æ–­ç»“束消息,这就意味着中断结束的回调里的处理逻辑可能会没有被执行到。
 æ‰€ä»¥éœ€è¦å…³æ³¨å½“切到前台运行状态时,是不是需要重新激活你的 Audio Session。
 */
- (void)willEnterForeground:(NSNotification*)notification{
    NSLog(@"willEnterForeground");
    //初次启动会走这个通知(根页面),这时候是没有进入后台的
    if (isBackGround) {
        return;
    }
    [self InterruptionEndedAVAudioSessionSetActiveYES];
    // è¿™é‡Œæ˜¯è€ƒè™‘到用户没有授权,之后通过AlertAction跳转到设置页面授权后再回到APP时做的重新检测
    //跳转到设置页面,授权后返回页面,继续初始化采集器
    if(!isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
    }
//    if (isAccessAudio && isAccessVideo) {
//        return;
//    }
//    if (isAccessVideo && !isAccessAudio) {
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
//    }else if (!isAccessVideo && isAccessAudio){
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//
//    }else if (!isAccessVideo && !isAccessAudio){
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//    }
}
- (void)willEnterBackground:(NSNotification *)notification {
    isBackGround = YES;
}
//授权Alert
-(void)requestAccessForAVMedia{
    if (!isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
    }
//    if (!isAccessVideo) {
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    }
    if (!iSAudioNotDetermined && iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"相机没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(!iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风与相机授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }
}
-(void)requestAccessForAVMediaType:(AVMediaType)type{
    if (type == AVMediaTypeVideo) {
        isAccessVideo = YES;
        iSVideoNotDetermined = YES;
    }else{
        isAccessAudio = YES;
        iSAudioNotDetermined = YES;
    }
    switch ([AVCaptureDevice authorizationStatusForMediaType:type])
    {
        case AVAuthorizationStatusAuthorized:
        {
            break;
        }
        case AVAuthorizationStatusNotDetermined:
        {
            dispatch_suspend(dispatch_get_main_queue());
            [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) {
                if (!granted) {
                    if (type == AVMediaTypeVideo) {
                        self->isAccessVideo = NO;
                    }else{
                        self->isAccessAudio = NO;
                    }
                }
                dispatch_resume(dispatch_get_main_queue());
            }];
            break;
        }
        default:
        {
            if (type == AVMediaTypeVideo) {
                isAccessVideo = NO;
                iSVideoNotDetermined = NO;
            }else{
                isAccessAudio = NO;
                iSAudioNotDetermined = NO;
            }
            break;
        }
    }
}
-(void)creatAlertViewWith:(NSString *)title message:(NSString *) msg cancel:(NSString *)cancelMsg{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:cancelMsg style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma ä¿å­˜å›¾ç‰‡åˆ°ç›¸å†Œ
- (void)saveImageToPhotosAlbum:(UIImage *)savedImage
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined)
    {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if(status == PHAuthorizationStatusAuthorized)
            {
                UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    }
    else
    {
        if (status == PHAuthorizationStatusAuthorized)
        {
            UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}
// æŒ‡å®šå›žè°ƒæ–¹æ³•
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *message = nil;
    if (!error) {
        message = saveToTheAlbumsStr;
    }
    else
    {
        message = operationFailedStr;
    }
    [self showUIAlertView:message];
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESOnUtlis.h
New file
@@ -0,0 +1,65 @@
//
//  ESOnUtlis.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#define TP_SYSTEM_VERSION ([[[UIDevice currentDevice] systemVersion] floatValue])
//#define IOS6 ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0)
#define IOS7  (TP_SYSTEM_VERSION >= 7.0)
#define IOS8  (TP_SYSTEM_VERSION >= 8.0)
#define IOS9  (TP_SYSTEM_VERSION >= 9.0)
#define IOS10 (TP_SYSTEM_VERSION >= 10.0)
//是否是 iPhoneX_XS_11Pro设备
#define IPhoneX ([UIScreen mainScreen].bounds.size.width == 375.0f && [UIScreen mainScreen].bounds.size.height == 812.0f)
//是否是 iPhoneXSMax_XR_11_11ProMax
#define IPhoneXR ([UIScreen mainScreen].bounds.size.width == 414.0f && [UIScreen mainScreen].bounds.size.height == 896.0f)
// æ˜¯å…¨é¢å±æ‰‹æœº
#define IsFullScreen (IPhoneX || IPhoneXR)
// Screen
#define APP_SCREEN_BOUNDS   [[UIScreen mainScreen] bounds]
#define APP_SCREEN_HEIGHT   (APP_SCREEN_BOUNDS.size.height)
#define APP_SCREEN_WIDTH    (APP_SCREEN_BOUNDS.size.width)
#define APP_STATUS_FRAME    [UIApplication sharedApplication].statusBarFrame
#define APP_TOP_BAR_HEIGHT    (IsFullScreen ? 88 : (IOS7 ? 64 : 44))
#define APP_STATUS_BAR_HEIGHT (IsFullScreen ? 44: (IOS7 ? 20 : 0))
#define APP_STATUS_BAR_HEIGHT_TURE (IsFullScreen ? 44: (IOS7 ? 0 : 0))
#define APP_TOOL_BAR_HEIGHT   49
#define APP_TAB_BAR_HEIGHT    (IsFullScreen ? (49 + 34): 49)
#define APP_CONTENT_WIDTH     (APP_SCREEN_BOUNDS.size.width)
#define APP_BUTTOM_HEIGHT     (IsFullScreen ? 34: 0)
#define APP_CONTENT_HEIGHT    (APP_SCREEN_HEIGHT - APP_TOP_BAR_HEIGHT - APP_TAB_BAR_HEIGHT)
#define APP_VISIBLE_HEIGHT    (APP_SCREEN_HEIGHT - APP_TOP_BAR_HEIGHT -APP_BUTTOM_HEIGHT)
#define APP_B_HEIGHT          (APP_SCREEN_HEIGHT - APP_BUTTOM_HEIGHT)
#define APP_UIFont            @"PingFangSC-Regular"
#define APP_UIFont_BOLD       @"PingFangSC-Semibold"
#define DesignWidth 375
#define DesignHeight 667
#define GetRealWidth(W)  W / (DesignWidth * 1.0f / APP_SCREEN_WIDTH)
#define GetRealHeight(H)  H / (DesignHeight * 1.0f / APP_SCREEN_HEIGHT)
#define TextColor    HEXCOLORA(0x333333, 1.0)
#define TextSelectColor    HEXCOLORA(0xFB744A, 1.0)
/** WEAKSELF_AT */
#define WEAKSELF_AT __weak __typeof(&*self)weakSelf_AT = self;
#define HEXCOLORA(rgbValue, a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 green:((float)((rgbValue & 0xFF00) >> 8)) / 255.0 blue:((float)(rgbValue & 0xFF)) / 255.0 alpha:a]
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnUtlis : NSObject
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESVideo.h
New file
@@ -0,0 +1,26 @@
//
//  ESVideo.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/28.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESVideo : NSObject
@property (nonatomic,strong) ESVideoPhone *es;
@property (nonatomic, copy) ImageCallback snapImageCallback;
+ (instancetype)shareInstance;
+ (void)haltSharedInstance;
-(void)initSDK;
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/ESVideo.m
New file
@@ -0,0 +1,78 @@
//
//  ESVideo.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/28.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import "ESVideo.h"
#import "ESOnUtlis.h"
#define WEAKSELF_AT __weak __typeof(&*self)weakSelf_AT = self;
@interface ESVideo()
@end
static ESVideo *_sharedInstance = nil;
static dispatch_once_t _onceToken;
@implementation ESVideo{
    ImageCallback mSnapImageCallback;
}
+ (instancetype)shareInstance {
    dispatch_once(&_onceToken, ^{
        _sharedInstance = [[ESVideo alloc] init];
        if(_sharedInstance) {
        }
        NSLog(@"ESVideo ShareInstance Did Create %p",_sharedInstance);
    });
    //NSLog(@"Access ESVideo %p",sharedInstance);
    return _sharedInstance;
}
+ (void)haltSharedInstance {
    if (_sharedInstance) {
//        if(_sharedInstance.es){
//            [_sharedInstance.es freeSubClass];
//        }
        _sharedInstance = nil;
        _onceToken = 0;
    }
}
- (instancetype)init {
    self = [super init];
    WEAKSELF_AT
    mSnapImageCallback = ^(UIImage *image){
        if(weakSelf_AT.snapImageCallback){
            weakSelf_AT.snapImageCallback(image);
        }
    };
    //门口机会有视频的长宽高,是固定的(暂时还不确定)
    _es = [[ESVideoPhone alloc]initESVideoPhoneWithFrame: CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, GetRealWidth(210)) delegate:nil imagecallBack:mSnapImageCallback];
    return self;
}
- (void)dealloc {
    NSLog(@"ESVideo SharedInstance Did Halted ");
}
-(void)initSDK{
     NSLog(@"ESVideo initSDK");
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/OpenViewController.h
New file
@@ -0,0 +1,17 @@
//
//  OpenViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/1.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OpenViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
On+/ESVideoOn/ESVideoPhoneSDKDemo/On/OpenViewController.m
New file
@@ -0,0 +1,161 @@
//
//  OpenViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/1.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import "OpenViewController.h"
#import "ESOnMonitorViewController.h"
#import "ESOnIntercomViewController.h"
#import "ESOnUtlis.h"
#import "ESVideo.h"
@interface OpenViewController ()
@property (nonatomic, strong) UIButton *monitorBtn;  //
@property (nonatomic, strong) UIButton *calledBtn;  //
@property (nonatomic,assign) BOOL                  isCollect;//是否收藏
@end
@implementation OpenViewController{
    NSString * DEVICEID;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [ESVideo.shareInstance initSDK];
    // Do any additional setup after loading the view.
    [self.view addSubview:self.monitorBtn];
    [self.view addSubview:self.calledBtn];
    //    DEVICEID = @"JJY000017XLUXX";
    DEVICEID = @"JJY000019VPLLF";
//         DEVICEID = @"JJY000016YWECG";
}
- (UIButton *)monitorBtn{
    if (_monitorBtn == nil) {
        _monitorBtn = [[UIButton alloc] initWithFrame:CGRectMake(20, 200, APP_SCREEN_WIDTH-40, 50)];
        _monitorBtn.backgroundColor = TextSelectColor;
        [_monitorBtn setTitle:@"监控" forState:UIControlStateNormal];
        _monitorBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _monitorBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_monitorBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        //        [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_monitorBtn addTarget:self action:@selector(monitorAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        //        [_monitorBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _monitorBtn.adjustsImageWhenHighlighted = NO;
        _monitorBtn.layer.cornerRadius = 25;
    }
    return _monitorBtn;
}
-(void)monitorAction{
    ESOnMonitorViewController *mVc = [[ESOnMonitorViewController alloc] init];
    //    mVc.modalPresentationStyle = UIModalPresentationFullScreen;
    //    mVc.edgesForExtendedLayout = YES;
    mVc.deviceName = @"室外机";
    mVc.roomName = @"2栋108";
    mVc.mESRoomID = 888;
    //    mVc.mESVideoID = @"JJY000007FSEYX";
    //    mVc.mESVideoID = @"JJY000016YWECG";
    //     mVc.mESVideoID = @"JJY000016YWECG";
    mVc.mESVideoID = DEVICEID;
    //    [self presentViewController:mVc  animated:YES completion:nil];
    [self.navigationController.navigationBar setHidden: YES];
    [self.navigationController pushViewController:mVc animated:YES];
}
- (UIButton *)calledBtn{
    if (_calledBtn == nil) {
        _calledBtn = [[UIButton alloc] initWithFrame:CGRectMake(20, 300, APP_SCREEN_WIDTH-40, 50)];
        _calledBtn.backgroundColor = TextSelectColor;
        [_calledBtn setTitle:@"被呼叫" forState:UIControlStateNormal];
        _calledBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _calledBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_calledBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        //        [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_calledBtn addTarget:self action:@selector(calledAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        //        [_monitorBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _calledBtn.adjustsImageWhenHighlighted = NO;
        _calledBtn.layer.cornerRadius = 25;
    }
    return _calledBtn;
}
-(void)calledAction{
    ESOnIntercomViewController *mVc = [[ESOnIntercomViewController alloc] init];
    //    mVc.modalPresentationStyle = UIModalPresentationFullScreen;
    //    mVc.edgesForExtendedLayout = YES;
    mVc.deviceName = @"室外机2";
    mVc.roomName = @"3栋808";
    mVc.mESRoomID = 888;
    //    mVc.mESVideoID = @"JJY000007FSEYX";
    //    mVc.mESVideoID = @"JJY000016YWECG";
    mVc.mESVideoID = DEVICEID;
//    mVc.isCollect = self.isCollect;
//
//    //    [self.navigationController pushViewController:mVc animated:YES];
//    mVc.collectButtonCallBack = ^(bool isCollect) {
//        self.isCollect = isCollect;
//        NSString *str;
//        if (isCollect) {
//            str = @"收藏";
//        }else{
//            str = @"取消收藏";
//        }
//        NSLog(@"CallBack%@",str);
//    };
    //    [self presentViewController:mVc  animated:YES completion:nil];
    [self.navigationController.navigationBar setHidden: YES];
    [self.navigationController pushViewController:mVc animated:YES];
}
/*
 #pragma mark - Navigation
 // In a storyboard-based application, you will often want to do a little preparation before navigation
 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 // Get the new view controller using [segue destinationViewController].
 // Pass the selected object to the new view controller.
 }
 */
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/SceneDelegate.h
New file
@@ -0,0 +1,16 @@
//
//  SceneDelegate.h
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property (strong, nonatomic) UIWindow * window;
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/SceneDelegate.m
New file
@@ -0,0 +1,67 @@
#import "SceneDelegate.h"
#import "OpenViewController.h"
@interface SceneDelegate ()
@end
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions  API_AVAILABLE(ios(13.0)){
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
    OpenViewController *mainVC = [[OpenViewController alloc] init];
    if (@available(iOS 13.0, *)) {
        UIWindowScene *windowScene = (UIWindowScene *)scene;
        self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
        self.window.frame = windowScene.coordinateSpace.bounds;
    } else {
        // Fallback on earlier versions
    }
    //    self.window.rootViewController = mainVC;
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:mainVC];
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
}
- (void)sceneDidDisconnect:(UIScene *)scene  API_AVAILABLE(ios(13.0)){
    // Called as the scene is being released by the system.
    // This occurs shortly after the scene enters the background, or when its session is discarded.
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
    // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
- (void)sceneDidBecomeActive:(UIScene *)scene  API_AVAILABLE(ios(13.0)){
    // Called when the scene has moved from an inactive state to an active state.
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
- (void)sceneWillResignActive:(UIScene *)scene  API_AVAILABLE(ios(13.0)){
    // Called when the scene will move from an active state to an inactive state.
    // This may occur due to temporary interruptions (ex. an incoming phone call).
}
- (void)sceneWillEnterForeground:(UIScene *)scene  API_AVAILABLE(ios(13.0)){
    // Called as the scene transitions from the background to the foreground.
    // Use this method to undo the changes made on entering the background.
}
- (void)sceneDidEnterBackground:(UIScene *)scene  API_AVAILABLE(ios(13.0)){
    // Called as the scene transitions from the foreground to the background.
    // Use this method to save data, release shared resources, and store enough scene-specific state information
    // to restore the scene back to its current state.
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/ViewController.h
New file
@@ -0,0 +1,23 @@
//
//  ViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
/*
[2019.12.12 15:56:16.217#40455][SendCustomData](/Users/maygion/corelooper/projects.oem/libvideophone/src/phone/phoneconnect.cpp:942)
###Warning[2019.12.12 15:56:16.217#40455][recv custom data=[{"command":"open","room_id":1234,"devType":7,"status":1}]](/Users/maygion/corelooper/projects.oem/libvideophone/src/phone/phoneconnect.cpp:1121)
2019-12-12 15:56:16.218004+0800 ESVideoPhoneSDKDemo[1724:882637] -[ESVideoPhone onPhoneEvent_UI:],msg=event=EVT_RECV_CUSTOM_DATA
data=eyJjb21tYW5kIjoib3BlbiIsInJvb21faWQiOjEyMzQsImRldlR5cGUiOjcsInN0YXR1cyI6MX0=
2019-12-12 15:56:16.218201+0800 ESVideoPhoneSDKDemo[1724:882637] äº‹ä»¶EVT_RECV_CUSTOM_DATA
*/
On+/ESVideoOn/ESVideoPhoneSDKDemo/ViewController.mm
New file
@@ -0,0 +1,556 @@
//
//  ViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
/*
 âš ï¸
 1、编译设置,
 BuildSettings设置 ï¼š
 Enable Bitcode è®¾ç½®ä¸ºNO;
 Other Linker Flags è®¾ç½®ä¸º-Wl,-all_load。
 2、framework
 æ·»åŠ ç³»ç»Ÿåº“ libiconv2.4.0.tbd,libz.tbd,libbz2.tbd.
 3、demo仅做参考,具体请根据App的情况适时调用
 */
#import <AVFoundation/AVFoundation.h>
#import "ViewController.h"
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
#import "AudioSessionHelper.h"
@interface ViewController ()<ESVideoPhoneDelegate,UITextFieldDelegate>
@property (nonatomic,strong) AudioSessionHelper    *sessionHelper;
@property (nonatomic,strong) ESVideoPhone          *es;
@property (nonatomic,assign) BOOL                  playing;
@property (nonatomic,assign) BOOL                  isInterrupt;
@property (nonatomic,assign) BOOL                  isSpeaking;
@property (nonatomic,strong) UIImage               *snapImage; //截图
@property (weak, nonatomic) IBOutlet UIButton *speakerBtn;
@property (weak, nonatomic) IBOutlet UITextField *uIDTextField;
@property (weak, nonatomic) IBOutlet UIButton *mCallOrAccept;
@property (weak, nonatomic) IBOutlet UIButton *monitorBtn;
@end
@implementation ViewController
{
    BOOL isAccessAudio;
    BOOL isAccessVideo;
    BOOL isBackGround;
    BOOL iSVideoNotDetermined;
    BOOL iSAudioNotDetermined;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化中断,进入后台的tag
    _playing = NO;
    _isSpeaking = NO;
    self.isInterrupt = NO;
    _es.isInterrupt = NO;
    isBackGround = NO;
    [_speakerBtn setTitle:@"听筒" forState:UIControlStateNormal];
//    _mCallOrAccept.enabled = NO;
//    _monitorBtn.enabled = NO;
    _uIDTextField.text = @"JJY000017XLUXX";//@"JJY000007FSEYX" é»˜è®¤é—¨å£æœºçš„ID
    _uIDTextField.delegate = self;    //⚠️这里必须要检查是否已经授权否则会失败,初始化视频音频采集
    [self requestAccessForAVMediaType:AVMediaTypeAudio];
     if (isAccessAudio) {
        ImageCallback snapImageCallback = ^(UIImage *image){
             //block是在分线程中调用的,这里要放到主线程
             dispatch_async(dispatch_get_main_queue(), ^{
                 self->_snapImage = image;
                 [self saveImage:image];
             });
         };
         //门口机会有视频的长宽高,是固定的(暂时还不确定)
         _es = [[ESVideoPhone alloc]initESVideoPhoneWithFrame:CGRectMake(10, 100, self.view.frame.size.width-20, (self.view.frame.size.width-20)/4*3) delegate:self imagecallBack:snapImageCallback];
         if (_es) {
             //判断视频渲染是否初始化成功,如果失败会走ESVideoPhoneDelegate方法
             if (_es.showView) {
                 _es.delegate = self;
                 [self.view addSubview:_es.showView];
             }
         }else{
             NSLog(@"ESVideoPhone åˆå§‹åŒ–失败");
             return;
         }
         // åˆå§‹åŒ–Audio采集Unit
         if(![_es initAudioCaptureSession]){
             return;
         }
     }else{
         //音频没有权限建议不要发起通话
         return;
     }
    //初始化视频采集Capture
    [self requestAccessForAVMediaType:AVMediaTypeVideo];
    if (isAccessVideo) {
        if(![_es initVideoCaptureSession]){
            NSLog(@"VideoCaptureSession åˆå§‹åŒ–失败");
        }
    }
    //初始化AudioSession
    _sessionHelper = [[AudioSessionHelper alloc]init];
    [_sessionHelper setAudioSession];
    //添加进入后台,中断等通知
    [self addObservers];
}
-(void)viewDidAppear:(BOOL)animated{
    //这个方法请根据App的具体情况调用
    //在viewDidLoad中 è°ƒç”¨requestAccessForAVMediaType: æ˜¯ä¸ºäº†èŠ‚çº¦åˆå§‹åŒ–çš„æ—¶é—´
    //在viewDidAppear中调用requestAccessForAVMediaType: æ˜¯ä¸ºäº†å¼¹å‡ºæç¤ºæ‰“开权限的Alert
//测试的时候发现如下情况:如果只把授权方法放到ViewDidAppear方法中处理,如果没有授权在初始化采集器时会失败。同样AlertView会因为View没有didLoad而导致present不出来
    if (!isAccessVideo || !isAccessAudio) {
        [self requestAccessForAVMedia];
    }
}
-(void)setIsInterrupt:(BOOL)isInterrupt{
    if (_es) {
        _es.isInterrupt = isInterrupt;
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    //防止用户不按挂断,或者不等收到对方的挂断,点击返回按钮。
    [_es onStopCapture];
    [_es stopTalk];
}
-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_es freeSubClass];
}
#pragma mark ButtonAction
//反呼:模拟门口机呼叫手机,需要门口机先点呼叫,等1-2S后,手机点反呼。
//params样本 address=192.168.1.3:8554,tag=mobile://123,
- (IBAction)onReverseCall:(id)sender {
    NSString *title = [sender titleForState:UIControlStateNormal];
    if ([title isEqualToString:@"反呼"]) {
       NSString *normalStr = _uIDTextField.text;
       NSString *param = [NSString stringWithFormat:@"address=%@,tag=mobile://123,",normalStr];
       //NSLog(@"%@", param);
       [_es onReverseCall:param];
    }else if([title isEqualToString:@"接听"]){
        [_es onAccept];
    }
}
- (IBAction)onHangup:(id)sender {
    [_es onHangup];
}
- (IBAction)onMonitor:(id)sender {
    [_es onMonitor:_uIDTextField.text];
}
- (IBAction)speaker:(UIButton *)sender {
    [_es stopTalk];
      NSString *result = nil;
      //听筒状态 æ’耳塞后拔掉后恢复到默认设置
      if (sender == nil) {
          result = [_sessionHelper speaker:NO];
      }else{
          if(!_isSpeaking){
              result = [_sessionHelper speaker:YES];
              _isSpeaking = YES;
          }else{
              result = [_sessionHelper speaker:NO];
              _isSpeaking = NO;
          }
      }
      if (result) {
          [sender setTitle:result forState:UIControlStateNormal];
          [_es startTalk];
      }
}
- (IBAction)openDoor:(id)sender {
    [_es openTheDoorWithRoomid: 1234];
}
- (IBAction)onSnap:(id)sender {
    [_es onSnap];
}
//⚠️ æ–‡æœ¬è¾“入框
#pragma mark ESVideoPhoneDelegate
//视频通话的状态代理事件,phoneEvent为返回的消息里面包含event状态与与event相关的数据
-(void)getPhoneEvent_UI:(NSString *)phoneEvent{
    NSLog(@"事件%@", phoneEvent);
    NSArray *strArray = [phoneEvent componentsSeparatedByString:@"\r\n"];
    NSArray *eventArray = [strArray.firstObject componentsSeparatedByString:@"="];
    NSString *phoneEventStr = eventArray.lastObject;
    if([phoneEventStr isEqual:@"EVT_Ringing"]){
           [_mCallOrAccept setTitle:@"接听" forState:UIControlStateNormal];
       }else if([phoneEventStr isEqual:@"EVT_StartStream"]){
       } else if([phoneEventStr isEqual:@"EVT_StopStream"]){
           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
       }else if([phoneEventStr isEqual:@"EVT_Connected"]){
           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
       }else if([phoneEventStr  isEqual:@"EVT_HangUp"]){
           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
       }else if([phoneEventStr  isEqual:@"EVT_P2POnlineStatusChanged"]){
           //EVT_P2PStarted(p2p初始化OK,可以连接),EVT_P2POnlineStatusChangedonline=1
           //p2p初始化成功,手机端目前没有这个回调了
          //_mCallOrAccept.enabled = YES;
          //_monitorBtn.enabled = YES;
       }else if([phoneEventStr  isEqual:@"EVT_RECV_CUSTOM_DATA"]){
           //开门的结果从这里返回
           NSString *baseStr = [strArray[1] substringFromIndex:5];
           NSData *data = [[NSData alloc]initWithBase64EncodedString:baseStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
           NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
           NSInteger status = [[dic valueForKey:@"status"]integerValue];
             if(status && status == 1){
                 NSLog(@"开门成功");
             }else{
                 NSLog(@"开门失败");
             }
       }
}
-(void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger)errortype{
    NSLog(@"错误%@", errorStr);
    //没有授权
    if (errortype == LMPVideoCaptureErrorNotAuthorized) {
        NSLog(@"错误%@", errorStr);
    }
}
#pragma mark AudioSession与Notifications处理
- (void) addObservers
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name: UIKeyboardWillChangeFrameNotification  object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
    //isAccessVideo,如果AVCaptureSession没有new出来不会收到通知
    if (isAccessVideo) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:nil];
    }else{
        //object:为nil å¯èƒ½ä¸ä¼šè§¦å‘通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:)
            name:AVAudioSessionInterruptionNotification object:[AVAudioSession
            sharedInstance]];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) sessionRuntimeError:(NSNotification*)notification
{
    NSError* error = notification.userInfo[AVCaptureSessionErrorKey];
    NSLog(@"Capture session runtime error: %@", error);
    // If media services were reset, and the last start succeeded, restart the session.
    if (error.code == AVErrorMediaServicesWereReset) {
        [_es onStopCapture];
        [_es startTalk];
    }
}
- (void)handleInterruption:(NSNotification *)notification
{
    NSUInteger interruptionType = [[[notification userInfo]
                                                        objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
      if (isBackGround) {
          return;
      }
        [_es stopTalk];
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
      if (self.isInterrupt == NO) {
          return;
      }else{
          //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
          [self InterruptionEndedAVAudioSessionSetActiveYES];
      }
    }
}
//AVAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
- (void) sessionWasInterrupted:(NSNotification*)notification
{
    if (_playing == YES) {
        self.isInterrupt = YES;
       //AVCaptureSessionInterruptionReason
        if (@available(iOS 9.0, *)) {
            NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; //电话中断是1
            NSLog(@"Capture session was interrupted with reason %ld", (long)reason);
            //音频硬件暂时不可用而造成的中断,例如,电话或警报。
            if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
                reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient");
                //VAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
                //Audio Queue Services, I/O audio unit
                [_es onStopCapture];
                [_es stopTalk];
                /*
                 NSError *error = nil;
                 [[AVAudioSession sharedInstance] setActive:NO error:&error];
                 if (error) {
                 NSLog(@"sessionWasInterruptedSetActiveNO error:%@", error);
                 }
                 */
            }else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground){
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground");
                //如果是电话中断,不会走进入后台的通知,进入后台再切换到前台这里是不用处理的
                if (isBackGround) {
                    return;
                }
                [_es onStopCapture];
                [_es stopTalk];
            }
            //多个应用程序资源争用质量下降。只有当应用程序占据全屏时,会话才能运行。
            else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps");
                // Fade-in a label to inform the user that the camera is unavailable.
            }else if (@available(iOS 11.1, *)) {
                if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure){
                    NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure");
                }
            } else {
                // Fallback on earlier versions
            }
        } else {
            if (isBackGround) {
                return;
            }
            [_es onStopCapture];
            [_es stopTalk];
        }
    }
}
//这个通知可能会获取不到,
- (void) sessionInterruptionEnded:(NSNotification*)notification
{
//    NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
    NSLog(@"Capture session interruption ended");
    if (self.isInterrupt == NO) {
        return;
    }else{
        //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
        [self InterruptionEndedAVAudioSessionSetActiveYES];
    }
}
-(void)InterruptionEndedAVAudioSessionSetActiveYES{
    if (isBackGround) {
        return;
    }
    if (self.isInterrupt == YES) {
        [_es onStartCapture];
        [_es startTalk];
        self.isInterrupt = NO;
    }
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
           //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            //免提状态下耳机插入没有采集,同意切换到默认状态
            NSLog(@"耳机插入");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"耳机拔出");
            if([[_speakerBtn titleForState:UIControlStateNormal] isEqualToString:@"免提"]){
                 [self speaker:nil];
            }else{
                [self speaker:_speakerBtn];
            }
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            //NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
/*
 éœ€è¦æ³¨æ„çš„æ˜¯ï¼Œæœ‰ä¸€ä¸ªä¸­æ–­å¼€å§‹æ¶ˆæ¯ä¸ä¸€å®šä¼šæœ‰ä¸€ä¸ªä¸­æ–­ç»“束消息,这就意味着中断结束的回调里的处理逻辑可能会没有被执行到。
 æ‰€ä»¥éœ€è¦å…³æ³¨å½“切到前台运行状态时,是不是需要重新激活你的 Audio Session。
 */
- (void)willEnterForeground:(NSNotification*)notification{
     NSLog(@"willEnterForeground");
    //初次启动会走这个通知(根页面),这时候是没有进入后台的
    if (isBackGround) {
        return;
    }
    [self InterruptionEndedAVAudioSessionSetActiveYES];
    // è¿™é‡Œæ˜¯è€ƒè™‘到用户没有授权,之后通过AlertAction跳转到设置页面授权后再回到APP时做的重新检测
    //跳转到设置页面,授权后返回页面,继续初始化采集器
    if (isAccessAudio && isAccessVideo) {
        return;
    }
    if (isAccessVideo && !isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
    }else if (!isAccessVideo && isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeVideo];
        if (isAccessVideo) {
            [_es initVideoCaptureSession];
        }
    }else if (!isAccessVideo && !isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        [self requestAccessForAVMediaType:AVMediaTypeVideo];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
        if (isAccessVideo) {
            [_es initVideoCaptureSession];
        }
    }
}
- (void)willEnterBackground:(NSNotification *)notification {
    isBackGround = YES;
}
//授权Alert
-(void)requestAccessForAVMedia{
    if (!isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
    }
    if (!isAccessVideo) {
         [self requestAccessForAVMediaType:AVMediaTypeVideo];
    }
    if (!iSAudioNotDetermined && iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"相机没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(!iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风与相机授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }
}
-(void)requestAccessForAVMediaType:(AVMediaType)type{
    if (type == AVMediaTypeVideo) {
        isAccessVideo = YES;
        iSVideoNotDetermined = YES;
    }else{
        isAccessAudio = YES;
        iSAudioNotDetermined = YES;
    }
    switch ([AVCaptureDevice authorizationStatusForMediaType:type])
    {
        case AVAuthorizationStatusAuthorized:
        {
            break;
        }
        case AVAuthorizationStatusNotDetermined:
        {
            dispatch_suspend(dispatch_get_main_queue());
            [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) {
                if (!granted) {
                    if (type == AVMediaTypeVideo) {
                        self->isAccessVideo = NO;
                    }else{
                        self->isAccessAudio = NO;
                    }
                }
                dispatch_resume(dispatch_get_main_queue());
            }];
            break;
        }
        default:
        {
            if (type == AVMediaTypeVideo) {
                isAccessVideo = NO;
                 iSVideoNotDetermined = NO;
            }else{
                isAccessAudio = NO;
                iSAudioNotDetermined = NO;
            }
           break;
        }
    }
}
-(void)creatAlertViewWith:(NSString *)title message:(NSString *) msg cancel:(NSString *)cancelMsg{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:cancelMsg style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }]];
   [self presentViewController:alertController animated:YES completion:nil];
}
#pragma mark --privita è¾…助
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self.view endEditing:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if(textField.returnKeyType == UIReturnKeyDone){
        [textField resignFirstResponder];//键盘收起
        return NO;
    }
    return YES;
}
- (void)keyboardWillChangeFrame:(NSNotification *)notification {
    NSValue *notiValue =notification.userInfo[@"UIKeyboardFrameEndUserInfoKey"];
     if(notiValue){
         CGRect rect = [notiValue CGRectValue];
         [UIView animateWithDuration:0.5 animations:^{
             if (self.view.frame.origin.y < 0) {
                 //已经上移动一次了,就不用移动了
                 if (self.view.frame.origin.y == 0){
                     CGFloat yFloat = self.view.frame.origin.y;
                     self.view.frame = CGRectMake(self.view.frame.origin.x, yFloat+50, self.view.frame.size.width, self.view.frame.size.height);
                 }
             }
             if (rect.origin.y == [UIScreen mainScreen].bounds.size.height) { //键盘将要收起
                 self.view.frame = CGRectMake(self.view.frame.origin.x, 0, self.view.frame.size.width, self.view.frame.size.height);
             }
         } completion:nil];
    }
}
- (void)saveImage:(UIImage *)image {
    NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    NSString *filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:
                          [NSString stringWithFormat:@"demo.png"]];  // ä¿å­˜æ–‡ä»¶çš„名称
    BOOL result =[UIImagePNGRepresentation(image)writeToFile:filePath   atomically:YES]; // ä¿å­˜æˆåŠŸä¼šè¿”å›žYES
    if (result == YES) {
        NSLog(@"保存成功");
    }
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemo/main.m
New file
@@ -0,0 +1,19 @@
//
//  main.m
//  ESVideoPhoneSDKDemo
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
On+/ESVideoOn/ESVideoPhoneSDKDemoTests/ESVideoPhoneSDKDemoTests.m
New file
@@ -0,0 +1,37 @@
//
//  ESVideoPhoneSDKDemoTests.m
//  ESVideoPhoneSDKDemoTests
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface ESVideoPhoneSDKDemoTests : XCTestCase
@end
@implementation ESVideoPhoneSDKDemoTests
- (void)setUp {
    // Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}
- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
    }];
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemoTests/Info.plist
New file
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
</dict>
</plist>
On+/ESVideoOn/ESVideoPhoneSDKDemoUITests/ESVideoPhoneSDKDemoUITests.m
New file
@@ -0,0 +1,48 @@
//
//  ESVideoPhoneSDKDemoUITests.m
//  ESVideoPhoneSDKDemoUITests
//
//  Created by eTouchSky on 2019/9/27.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface ESVideoPhoneSDKDemoUITests : XCTestCase
@end
@implementation ESVideoPhoneSDKDemoUITests
- (void)setUp {
    // Put setup code here. This method is called before the invocation of each test method in the class.
    // In UI tests it is usually best to stop immediately when a failure occurs.
    self.continueAfterFailure = NO;
    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
- (void)tearDown {
    // Put teardown code here. This method is called after the invocation of each test method in the class.
}
- (void)testExample {
    // UI tests must launch the application that they test.
    XCUIApplication *app = [[XCUIApplication alloc] init];
    [app launch];
    // Use recording to get started writing UI tests.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testLaunchPerformance {
    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
        // This measures how long it takes to launch your application.
        [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{
            [[[XCUIApplication alloc] init] launch];
        }];
    }
}
@end
On+/ESVideoOn/ESVideoPhoneSDKDemoUITests/Info.plist
New file
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
</dict>
</plist>
On+/ESVideoOn/ESVideoPhoneSDk.framework/ESVideoPhoneSDk
Binary files differ
On+/ESVideoOn/ESVideoPhoneSDk.framework/Headers/ESError.h
New file
@@ -0,0 +1,76 @@
//
//  ESError.h
//  ESVideoPhoneSDk
//
//  Created by eTouchSky on 2019/12/14.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
//  ESVideoPhone的ESVideoPhoneDelegate方法“- (void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger) errortype” ä¸­errortype的类型
#import <Foundation/Foundation.h>
//视频采集,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
//App需要处理 LMPVideoCaptureErrorGetDevice与LMPVideoCaptureErrorNotAuthorized ç±»åž‹çš„错误
typedef NS_ENUM (NSUInteger, LMPVideoCaptureError){
    LMPVideoCaptureErrorGetDevice                      = 0,//获取摄像头失败
    LMPVideoCaptureErrorGetInput                       = 1,//获取视频输入错误,从error中获取具体信息
    LMPVideoCaptureErrorAddInput                       = 2,//AddInputVideo错误
    LMPVideoCaptureErrorAddOutput                      = 3,//AddOutputVideo错误
    LMPVideoCaptureErrorNotAuthorized                  = 4,//没有授权,授权处理由App实现
    LMPVideoCaptureErrorSetSessionPreset               = 5,//SetSessionPreset失败
};
//openGl视频渲染,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
typedef NS_ENUM (NSUInteger, LMPOpenglError){
    LMPOpenglErrorSetCurrentContext                      = 0,//EAGLContext上下文创建失败
    LMPOpenglErrorRenderbuffer                           = 1,//OpenGL attach渲染缓冲区失败
    LMPOpenglErrorGenTextures                            = 2,//OpenGL çº¹ç†åˆ›å»ºå¤±è´¥
    LMPOpenglErrorShaderConnection                       = 3,//OpenGL ç€è‰²å™¨è¿žæŽ¥å¤±è´¥
    LMPOpenglErrorLoadShader                             = 4,//OpenGL åŠ è½½ç€è‰²å™¨å¤±è´¥
    LMPOpenglErrorShaderCompilation                      = 5,//OpenGL ç€è‰²å™¨ç¼–译失败
};
/**视频解码,会导致App看不到门口机的视频
debug模式下,断点可能导致出现LMPH264HwDecoderErrorKVTInvalidSessionErr的错误
前台后切换,来电等原因的中断可能会出现LMPH264HwDecoderErrorKVTInvalidSessionErr的错误(已经修复,注意测试过程中是否还会出现)
*/
typedef NS_ENUM (NSUInteger, LMPH264HwDecoderError){
    LMPH264HwDecoderErrorInitH264HardwareDecoder                     = 0,//InitH264HardwareDecoderErr
    LMPH264HwDecoderErrorKVTVideoDecoderMalfunction                  = 1,//kVTVideoDecoderMalfunctionErr
    LMPH264HwDecoderErrorKVTInvalidSessionErr                        = 2,//kVTInvalidSessionErr
    LMPH264HwDecoderErrorKVTVideoDecoderBadDataErr                   = 3,//kVTVideoDecoderBadDataErr
    LMPH264HwDecoderErrorDecodeOtherErr                              = 4,//硬解码其他错误
    LMPH264HwDecoderErrorCVPixelBufferRefPlantErr                    = 5,//CVPixelBufferRef中数据为空
    LMPH264HwDecoderErrorCVPixelBufferRefYUVErr                      = 6,//CVPixelBufferRef中取出YUV失败
};
//视频编码,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
typedef NS_ENUM (NSUInteger, LMPH264EncoderError){
    LMPH264EncoderErrorVTCompressionSessionCreate                    = 0,//VTCompressionSessionCreateErr
    LMPH264EncoderErrorVideoCompressonOutputCallback                 = 1,//编码回调失败
};
//音频采集,会导致不能采集音频,对方听不到App的声音(监控状态时,门口机是听不到App的声音的)
typedef NS_ENUM (NSUInteger, LMPAudioCaptureError){
    LMPAudioCaptureErrorGetNew                         = 0,//AudioComponentInstanceNew失败
    LMPAudioCaptureErrorStreamFormat                   = 1,//kAudioUnitProperty_StreamFormat失败
    LMPAudioCaptureErrorEnableRecord                   = 2,//打开录音失败
    LMPAudioCaptureErrorEnablePlay                     = 3,//关闭播放失败
    LMPAudioCaptureErrorSetInputCallback               = 4,//AudioUnitSetCallback失败
    LMPAudioCaptureErrorShouldAllocateBuffer           = 5,//使用自己的缓存失败
    LMPAudioCaptureErrorBypassVoiceProcessing          = 6,//打开回声消除功能失败
    LMPAudioCaptureErrorAudioUnitInitialize          = 7,//AudioUnitInitialize失败
};
//音频播放,会导致不能播放音频,听不到对方的声音
typedef NS_ENUM (NSUInteger, LMPAudioPlayError){
    LMPAudioPlayErrorGetNew                         = 0,//AudioPlay_AudioComponentInstanceNew失败
    LMPAudioPlayErrorStreamFormat                   = 1,//AudioPlay_kAudioUnitProperty_StreamFormat失败
    LMPAudioPlayErrorEnableRecord                   = 2,//AudioPlay_关闭录音失败
    LMPAudioPlayErrorEnablePlay                     = 3,//AudioPlay_打开播放失败
    LMPAudioPlayErrorSetRenderCallback              = 4,//AudioPlay_AudioUnitSetRenderCallback失败
    LMPAudioPlayErrorShouldAllocateBuffer           = 5,//使用自己的缓存失败
    LMPAudioPlayErrorBypassVoiceProcessing          = 6,//AudioPlay_打开回声消除功能失败
    LMPAudioPlayErrorAudioUnitInitialize            = 7,//AudioPlay_AudioUnitInitialize失败
};
@interface ESError : NSObject
@end
On+/ESVideoOn/ESVideoPhoneSDk.framework/Headers/ESVideoPhone.h
New file
@@ -0,0 +1,83 @@
//  ESVideoPhone.h
//  VideoPhone
//
//  Created by maygion on 2019/3/26.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//  SDK版本号:1.0.0  æµ‹è¯•版1
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void (^ImageCallback)(UIImage *image);
@protocol ESVideoPhoneDelegate <NSObject>
- (void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger) errortype;
- (void)getPhoneEvent_UI:(NSString*)phoneEvent;
@end
@interface ESVideoPhone : NSObject
//错误信息
@property(nonatomic,weak) id<ESVideoPhoneDelegate> delegate;
////获取视频截图的block
//@property(nonatomic,copy) ImageCallback snapImageCallback;
//视频显示的View âš ï¸è¯·åšéžç©ºåˆ¤æ–­
@property(nonatomic,strong,readonly) UIView* showView;
//中断设置
@property (nonatomic,assign,readwrite) BOOL isInterrupt;
/**
* ESVideoPhone åˆå§‹åŒ–方法
*
* @param frame ç”Ÿæˆæ˜¾ç¤ºè§†é¢‘çš„CGRect
* @param delegate ESVideoPhoneDelegate代理
* @paramimagecallBack èŽ·å–è§†é¢‘æˆªå›¾çš„block
*
*  @return ESVideoPhone å®žä¾‹,返回nil表示初始化失败。
*/
- (instancetype)initESVideoPhoneWithFrame:(CGRect)frame delegate: (id<ESVideoPhoneDelegate>)del imagecallBack:(ImageCallback)callBack;
//授权后打开音频采集,视频没有权限也可以只音频通话
-(BOOL)initAudioCaptureSession;
//⚠️授权后打开视频采集,音频没有权限建议不要发起通话
-(BOOL)initVideoCaptureSession;
/**
* ç›‘控
*
* @param deviceId é—¨å£æœºè®¾å¤‡ID,这个参数会从后台拉取(一般是个数组其中包含该用户权限拥有的所有门口机)
*/
- (void)onMonitor:(NSString *)deviceId;
/**
* æŽ¥å¬
* è¿™ä¸ªæ–¹æ³•会在AppDelegate的通知接口中调用,采用小米推送
* @param deviceId é—¨å£æœºè®¾å¤‡ID,这个参数会从通知中获取由设备ID与单次通话tag拼成(可能会有变动)
*/
- (void)onReverseCall:(NSString *)deviceId;
//接听
- (void)onAccept;
//挂断
- (void)onHangup;
//打开音频采集与音频播放
-(void)startTalk;
//关闭音频采集与音频播放
- (void)stopTalk;
//获取视频截图
- (void)onSnap;
//打开视频采集
- (void)onStartCapture;
//关闭视频采集
- (void)onStopCapture;
/**
* å¼€é—¨ï¼Œç»“果在ESVideoPhoneDelegate æ–¹æ³•中返回
*
* @param roomId ä½æˆ·çš„roomid)
*/
-(void)openTheDoorWithRoomid: (int)roomId;
//释放资源(delegate释放等)
-(void)freeSubClass;
@end
On+/ESVideoOn/ESVideoPhoneSDk.framework/Info.plist
Binary files differ
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeDirectory
Binary files differ
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements
Binary files differ
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements-1
Binary files differ
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeResources
New file
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>files</key>
    <dict>
        <key>Headers/ESError.h</key>
        <data>
        6x23lohs1gRaX2F7raZcezxZt4A=
        </data>
        <key>Headers/ESVideoPhone.h</key>
        <data>
        PXntC+GjEn5A+q23YZHDZYIGj3k=
        </data>
        <key>Info.plist</key>
        <data>
        pac6jrrTIT3Ur/3XHh3CoCOUlnE=
        </data>
    </dict>
    <key>files2</key>
    <dict>
        <key>Headers/ESError.h</key>
        <dict>
            <key>hash</key>
            <data>
            6x23lohs1gRaX2F7raZcezxZt4A=
            </data>
            <key>hash2</key>
            <data>
            y8ReVa9YaZ6fyPjy8EeTl3jbaUadZUYVC0IdLJ/HrgM=
            </data>
        </dict>
        <key>Headers/ESVideoPhone.h</key>
        <dict>
            <key>hash</key>
            <data>
            PXntC+GjEn5A+q23YZHDZYIGj3k=
            </data>
            <key>hash2</key>
            <data>
            MboPVH5djlpgR//Z7UcRBMEeaakW6FWQbZxlkx4KzaM=
            </data>
        </dict>
    </dict>
    <key>rules</key>
    <dict>
        <key>^.*</key>
        <true/>
        <key>^.*\.lproj/</key>
        <dict>
            <key>optional</key>
            <true/>
            <key>weight</key>
            <real>1000</real>
        </dict>
        <key>^.*\.lproj/locversion.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>1100</real>
        </dict>
        <key>^Base\.lproj/</key>
        <dict>
            <key>weight</key>
            <real>1010</real>
        </dict>
        <key>^version.plist$</key>
        <true/>
    </dict>
    <key>rules2</key>
    <dict>
        <key>.*\.dSYM($|/)</key>
        <dict>
            <key>weight</key>
            <real>11</real>
        </dict>
        <key>^(.*/)?\.DS_Store$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>2000</real>
        </dict>
        <key>^.*</key>
        <true/>
        <key>^.*\.lproj/</key>
        <dict>
            <key>optional</key>
            <true/>
            <key>weight</key>
            <real>1000</real>
        </dict>
        <key>^.*\.lproj/locversion.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>1100</real>
        </dict>
        <key>^Base\.lproj/</key>
        <dict>
            <key>weight</key>
            <real>1010</real>
        </dict>
        <key>^Info\.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^PkgInfo$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^embedded\.provisionprofile$</key>
        <dict>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^version\.plist$</key>
        <dict>
            <key>weight</key>
            <real>20</real>
        </dict>
    </dict>
</dict>
</plist>
On+/ESVideoOn/ESVideoPhoneSDk.framework/_CodeSignature/CodeSignature
Binary files differ
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_answer.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_back.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_hangup.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_takephoto_select.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_takephoto_unselect.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_unlock_select.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ic_esvideo_on_unlock_unselect.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/ͼÏñ 83@2x.png
On+/ON+¿ÉÊÓ¶Ô½²Í¼±ê/¾ØÐÎ 1788@2x.png
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.pbxproj
New file
@@ -0,0 +1,361 @@
// !$*UTF8*$!
{
    archiveVersion = 1;
    classes = {
    };
    objectVersion = 52;
    objects = {
/* Begin PBXBuildFile section */
        B9506A2025F9EE8800F6FD4F /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9506A1F25F9EE8800F6FD4F /* libbz2.tbd */; platformFilter = ios; };
        B9506A2325F9EE9000F6FD4F /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9506A2225F9EE9000F6FD4F /* libz.tbd */; platformFilter = ios; };
        B9506A2625F9EE9800F6FD4F /* libiconv.2.4.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9506A2525F9EE9800F6FD4F /* libiconv.2.4.0.tbd */; platformFilter = ios; };
        B9506A3325F9EEE900F6FD4F /* CBToast.m in Sources */ = {isa = PBXBuildFile; fileRef = B9506A2A25F9EEE900F6FD4F /* CBToast.m */; platformFilter = ios; };
        B9506A3425F9EEE900F6FD4F /* ESOnMonitorViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B9506A2C25F9EEE900F6FD4F /* ESOnMonitorViewController.m */; platformFilter = ios; };
        B9506A3525F9EEE900F6FD4F /* AudioSessionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = B9506A2D25F9EEE900F6FD4F /* AudioSessionHelper.m */; platformFilter = ios; };
        B9506A3625F9EEE900F6FD4F /* ESOnIntercomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B9506A2E25F9EEE900F6FD4F /* ESOnIntercomViewController.m */; platformFilter = ios; };
        B9506A3725F9EEE900F6FD4F /* ESVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = B9506A3125F9EEE900F6FD4F /* ESVideo.m */; platformFilter = ios; };
        B9506A4725F9F0E300F6FD4F /* ESVideoPhoneSDk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9506A4625F9F0E300F6FD4F /* ESVideoPhoneSDk.framework */; platformFilter = ios; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
        B9506A0D25F9EE3100F6FD4F /* CopyFiles */ = {
            isa = PBXCopyFilesBuildPhase;
            buildActionMask = 2147483647;
            dstPath = "include/$(PRODUCT_NAME)";
            dstSubfolderSpec = 16;
            files = (
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
        B9506A0F25F9EE3100F6FD4F /* libESVideoOnXamarin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libESVideoOnXamarin.a; sourceTree = BUILT_PRODUCTS_DIR; };
        B9506A1F25F9EE8800F6FD4F /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/usr/lib/libbz2.tbd; sourceTree = DEVELOPER_DIR; };
        B9506A2225F9EE9000F6FD4F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
        B9506A2525F9EE9800F6FD4F /* libiconv.2.4.0.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.2.4.0.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/usr/lib/libiconv.2.4.0.tbd; sourceTree = DEVELOPER_DIR; };
        B9506A2825F9EEE900F6FD4F /* AudioSessionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioSessionHelper.h; sourceTree = "<group>"; };
        B9506A2925F9EEE900F6FD4F /* ESOnIntercomViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnIntercomViewController.h; sourceTree = "<group>"; };
        B9506A2A25F9EEE900F6FD4F /* CBToast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBToast.m; sourceTree = "<group>"; };
        B9506A2B25F9EEE900F6FD4F /* ESOnMonitorViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnMonitorViewController.h; sourceTree = "<group>"; };
        B9506A2C25F9EEE900F6FD4F /* ESOnMonitorViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESOnMonitorViewController.m; sourceTree = "<group>"; };
        B9506A2D25F9EEE900F6FD4F /* AudioSessionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioSessionHelper.m; sourceTree = "<group>"; };
        B9506A2E25F9EEE900F6FD4F /* ESOnIntercomViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESOnIntercomViewController.m; sourceTree = "<group>"; };
        B9506A2F25F9EEE900F6FD4F /* ESVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESVideo.h; sourceTree = "<group>"; };
        B9506A3025F9EEE900F6FD4F /* CBToast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBToast.h; sourceTree = "<group>"; };
        B9506A3125F9EEE900F6FD4F /* ESVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ESVideo.m; sourceTree = "<group>"; };
        B9506A3225F9EEE900F6FD4F /* ESOnUtlis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESOnUtlis.h; sourceTree = "<group>"; };
        B9506A4625F9F0E300F6FD4F /* ESVideoPhoneSDk.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ESVideoPhoneSDk.framework; path = Extern/ESVideoPhoneSDk.framework; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
        B9506A0C25F9EE3100F6FD4F /* Frameworks */ = {
            isa = PBXFrameworksBuildPhase;
            buildActionMask = 2147483647;
            files = (
                B9506A4725F9F0E300F6FD4F /* ESVideoPhoneSDk.framework in Frameworks */,
                B9506A2625F9EE9800F6FD4F /* libiconv.2.4.0.tbd in Frameworks */,
                B9506A2325F9EE9000F6FD4F /* libz.tbd in Frameworks */,
                B9506A2025F9EE8800F6FD4F /* libbz2.tbd in Frameworks */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
        B9506A0625F9EE3100F6FD4F = {
            isa = PBXGroup;
            children = (
                B9506A1125F9EE3100F6FD4F /* ESVideoOnXamarin */,
                B9506A1025F9EE3100F6FD4F /* Products */,
                B9506A1C25F9EE7B00F6FD4F /* Frameworks */,
            );
            sourceTree = "<group>";
        };
        B9506A1025F9EE3100F6FD4F /* Products */ = {
            isa = PBXGroup;
            children = (
                B9506A0F25F9EE3100F6FD4F /* libESVideoOnXamarin.a */,
            );
            name = Products;
            sourceTree = "<group>";
        };
        B9506A1125F9EE3100F6FD4F /* ESVideoOnXamarin */ = {
            isa = PBXGroup;
            children = (
                B9506A2825F9EEE900F6FD4F /* AudioSessionHelper.h */,
                B9506A2D25F9EEE900F6FD4F /* AudioSessionHelper.m */,
                B9506A3025F9EEE900F6FD4F /* CBToast.h */,
                B9506A2A25F9EEE900F6FD4F /* CBToast.m */,
                B9506A2925F9EEE900F6FD4F /* ESOnIntercomViewController.h */,
                B9506A2E25F9EEE900F6FD4F /* ESOnIntercomViewController.m */,
                B9506A2B25F9EEE900F6FD4F /* ESOnMonitorViewController.h */,
                B9506A2C25F9EEE900F6FD4F /* ESOnMonitorViewController.m */,
                B9506A3225F9EEE900F6FD4F /* ESOnUtlis.h */,
                B9506A2F25F9EEE900F6FD4F /* ESVideo.h */,
                B9506A3125F9EEE900F6FD4F /* ESVideo.m */,
            );
            path = ESVideoOnXamarin;
            sourceTree = "<group>";
        };
        B9506A1C25F9EE7B00F6FD4F /* Frameworks */ = {
            isa = PBXGroup;
            children = (
                B9506A4625F9F0E300F6FD4F /* ESVideoPhoneSDk.framework */,
                B9506A2525F9EE9800F6FD4F /* libiconv.2.4.0.tbd */,
                B9506A2225F9EE9000F6FD4F /* libz.tbd */,
                B9506A1F25F9EE8800F6FD4F /* libbz2.tbd */,
            );
            name = Frameworks;
            sourceTree = "<group>";
        };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
        B9506A0E25F9EE3100F6FD4F /* ESVideoOnXamarin */ = {
            isa = PBXNativeTarget;
            buildConfigurationList = B9506A1825F9EE3100F6FD4F /* Build configuration list for PBXNativeTarget "ESVideoOnXamarin" */;
            buildPhases = (
                B9506A0B25F9EE3100F6FD4F /* Sources */,
                B9506A0C25F9EE3100F6FD4F /* Frameworks */,
                B9506A0D25F9EE3100F6FD4F /* CopyFiles */,
            );
            buildRules = (
            );
            dependencies = (
            );
            name = ESVideoOnXamarin;
            productName = ESVideoOnXamarin;
            productReference = B9506A0F25F9EE3100F6FD4F /* libESVideoOnXamarin.a */;
            productType = "com.apple.product-type.library.static";
        };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
        B9506A0725F9EE3100F6FD4F /* Project object */ = {
            isa = PBXProject;
            attributes = {
                LastUpgradeCheck = 1220;
                TargetAttributes = {
                    B9506A0E25F9EE3100F6FD4F = {
                        CreatedOnToolsVersion = 12.2;
                    };
                };
            };
            buildConfigurationList = B9506A0A25F9EE3100F6FD4F /* Build configuration list for PBXProject "ESVideoOnXamarin" */;
            compatibilityVersion = "Xcode 9.3";
            developmentRegion = en;
            hasScannedForEncodings = 0;
            knownRegions = (
                en,
                Base,
            );
            mainGroup = B9506A0625F9EE3100F6FD4F;
            productRefGroup = B9506A1025F9EE3100F6FD4F /* Products */;
            projectDirPath = "";
            projectRoot = "";
            targets = (
                B9506A0E25F9EE3100F6FD4F /* ESVideoOnXamarin */,
            );
        };
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
        B9506A0B25F9EE3100F6FD4F /* Sources */ = {
            isa = PBXSourcesBuildPhase;
            buildActionMask = 2147483647;
            files = (
                B9506A3625F9EEE900F6FD4F /* ESOnIntercomViewController.m in Sources */,
                B9506A3725F9EEE900F6FD4F /* ESVideo.m in Sources */,
                B9506A3325F9EEE900F6FD4F /* CBToast.m in Sources */,
                B9506A3525F9EEE900F6FD4F /* AudioSessionHelper.m in Sources */,
                B9506A3425F9EEE900F6FD4F /* ESOnMonitorViewController.m in Sources */,
            );
            runOnlyForDeploymentPostprocessing = 0;
        };
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
        B9506A1625F9EE3100F6FD4F /* 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
                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;
                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 = 10.0;
                MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
                MTL_FAST_MATH = YES;
                ONLY_ACTIVE_ARCH = YES;
                SDKROOT = iphoneos;
            };
            name = Debug;
        };
        B9506A1725F9EE3100F6FD4F /* 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
                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;
                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 = 10.0;
                MTL_ENABLE_DEBUG_INFO = NO;
                MTL_FAST_MATH = YES;
                SDKROOT = iphoneos;
                VALIDATE_PRODUCT = YES;
            };
            name = Release;
        };
        B9506A1925F9EE3100F6FD4F /* Debug */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                ARCHS = "$(ARCHS_STANDARD)";
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 259RU5K4MU;
                ENABLE_BITCODE = NO;
                FRAMEWORK_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/Extern",
                );
                LIBRARY_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/Extern/ESVideoPhoneSDk.framework",
                );
                OTHER_LDFLAGS = "-Wl,-all_load";
                PRODUCT_NAME = "$(TARGET_NAME)";
                SKIP_INSTALL = YES;
                TARGETED_DEVICE_FAMILY = "1,2";
            };
            name = Debug;
        };
        B9506A1A25F9EE3100F6FD4F /* Release */ = {
            isa = XCBuildConfiguration;
            buildSettings = {
                ARCHS = arm64;
                CODE_SIGN_STYLE = Automatic;
                DEVELOPMENT_TEAM = 259RU5K4MU;
                ENABLE_BITCODE = NO;
                FRAMEWORK_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/Extern",
                );
                LIBRARY_SEARCH_PATHS = (
                    "$(inherited)",
                    "$(PROJECT_DIR)/Extern/ESVideoPhoneSDk.framework",
                );
                ONLY_ACTIVE_ARCH = NO;
                OTHER_LDFLAGS = "-Wl,-all_load";
                PRODUCT_NAME = "$(TARGET_NAME)";
                SKIP_INSTALL = YES;
                TARGETED_DEVICE_FAMILY = "1,2";
            };
            name = Release;
        };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
        B9506A0A25F9EE3100F6FD4F /* Build configuration list for PBXProject "ESVideoOnXamarin" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                B9506A1625F9EE3100F6FD4F /* Debug */,
                B9506A1725F9EE3100F6FD4F /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
        B9506A1825F9EE3100F6FD4F /* Build configuration list for PBXNativeTarget "ESVideoOnXamarin" */ = {
            isa = XCConfigurationList;
            buildConfigurations = (
                B9506A1925F9EE3100F6FD4F /* Debug */,
                B9506A1A25F9EE3100F6FD4F /* Release */,
            );
            defaultConfigurationIsVisible = 0;
            defaultConfigurationName = Release;
        };
/* End XCConfigurationList section */
    };
    rootObject = B9506A0725F9EE3100F6FD4F /* Project object */;
}
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
New file
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>IDEDidComputeMac32BitWarning</key>
    <true/>
</dict>
</plist>
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin.xcodeproj/xcshareddata/xcschemes/ESVideoOnXamarin.xcscheme
New file
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1220"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "B9506A0E25F9EE3100F6FD4F"
               BuildableName = "libESVideoOnXamarin.a"
               BlueprintName = "ESVideoOnXamarin"
               ReferencedContainer = "container:ESVideoOnXamarin.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Release"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "B9506A0E25F9EE3100F6FD4F"
            BuildableName = "libESVideoOnXamarin.a"
            BlueprintName = "ESVideoOnXamarin"
            ReferencedContainer = "container:ESVideoOnXamarin.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/AudioSessionHelper.h
New file
@@ -0,0 +1,19 @@
//
//  AudioSessionHelper.h
//  VideoPhone
//
//  Created by eTouchSky on 2019/5/30.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface AudioSessionHelper : NSObject
- (void)setAudioSession;
-(NSString *)speaker:(BOOL)isSpeaking;
@end
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/AudioSessionHelper.m
New file
@@ -0,0 +1,124 @@
//
//  AudioSessionHelper.m
//  VideoPhone
//
//  Created by eTouchSky on 2019/5/30.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
#import "AudioSessionHelper.h"
#import <AVFoundation/AVFoundation.h>
@interface AudioSessionHelper()
@property (nonatomic,assign) BOOL                  speaking;
@end
@implementation AudioSessionHelper
#pragma mark -- AVAudioSession的设置
-(void)setAudioSession{
    NSError *error = nil;
    //免提播放 æœ‰å›žå£°ï¼š ç½‘络引起回音(两个手机隔太近就会很大)。 æ‰‹æœºæœ¬èº«çš„设计有问题(录播),关闭降噪功能(关闭后基本没有了)
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    if (error) {
        NSLog(@"AVAudioSessionCategoryPlayAndRecord error:%@", error);
    }else{
        NSLog(@"听筒播放");
    }
    [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
    [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:0.05 error:&error];
    if (error) {
        NSLog(@"AVAudioSessionSetPreferredIOBufferDuration error:%@", error);
    }
    /**< ç çŽ‡è®¾ç½®ä¸æ­£ç¡®ä¼šå¯¼è‡´æ’­æ”¾å‡ºæ¥çš„å£°éŸ³å˜å°–æˆ–å˜å¹³ï¼Œæœ‰ä¸€ç‚¹æ‹–éŸ³æˆ–è€…æœ€åŽä¸€ç‚¹éŸ³å°‘äº†,8000的采样率设置16000的码率很好>*/
    [[AVAudioSession sharedInstance] setPreferredSampleRate:16000 error:&error];
    if (error) {
        NSLog(@"setPreferredSampleRate error:%@", error);
    }
}
//必须先setActive:NO  å†è®¾ç½®option  å†setActive:YES, å¦åˆ™æœ‰äº›ç³»ç»Ÿï¼Œæœ‰äº›ç•¸å½¢ï¼Œåœ¨å¬ç­’模式下不能采集
//这里打断点测试可能会报错:p2p终端,解码器终端。
-(NSString *)speaker:(BOOL)isSpeaking{
    NSError *error = nil;
    [[AVAudioSession sharedInstance] setActive:NO error:&error];
    if (error) {
        NSLog(@"setActive:NO error:%@", error);
    }
    if (isSpeaking == NO) {
        //听筒播放 æ•ˆæžœå¾ˆå¥½
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
        if (error) {
            NSLog(@"AVAudioSessionCategoryPlayAndRecord error:%@", error);
            return nil;
        }else{
            NSLog(@"听筒播放");
             [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
            if (error) {
                NSLog(@"setActive:YES error:%@", error);
            }
           [self LogAudioSessionInfo];
            return @"免提";
        }
    }else{
        //免提播放 æœ‰å›žå£°ï¼š ç½‘络引起回音(两个手机隔太近就会很大)。 æ‰‹æœºæœ¬èº«çš„设计有问题(录播),关闭降噪功能(关闭后基本没有了)
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
        if (error) {
            NSLog(@"AVAudioSessionCategoryOptionDefaultToSpeaker error:%@", error);
            return nil;
        }else{
            NSLog(@"免提播放");
             [[AVAudioSession sharedInstance] setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:&error];
            if (error) {
                NSLog(@"setActive:YES error:%@", error);
            }
            //[self LogAudioSessionInfo];
            return @"听筒";
        }
    }
}
-(void)LogAudioSessionInfo{
    AVAudioSessionCategory category = [[AVAudioSession sharedInstance] category];
    AVAudioSessionCategoryOptions option = [[AVAudioSession sharedInstance] categoryOptions];
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    NSArray* outputArray = [AVAudioSession sharedInstance].currentRoute.outputs;
    NSLog(@"%@", category);
    NSLog(@"%lu", (unsigned long)option);
    NSLog(@"%@", inputArray);
    NSLog(@"%@", outputArray);
}
/*
-(void)changeMicToHeadset{
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    for (AVAudioSessionPortDescription* desc in inputArray) {
        if ([desc.portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
            NSError* error;
            [[AVAudioSession sharedInstance] setPreferredInput:desc error:&error];
        }
    }
}
-(void)changeMicToBuiltIn{
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    for (AVAudioSessionPortDescription* desc in inputArray) {
        if ([desc.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
            NSError* error;
            [[AVAudioSession sharedInstance] setPreferredInput:desc error:&error];
        }
    }
}
*/
@end
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/CBToast.h
New file
@@ -0,0 +1,21 @@
//
//  CBToast.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/10.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface CBToast : NSObject
//显示消息-->default center
+ (void)showToastAction:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/CBToast.m
New file
@@ -0,0 +1,110 @@
//
//  CBToast.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/10.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import "CBToast.h"
@implementation CBToast
static UILabel *toastLabel = nil;
+ (UILabel *)currentToastLabel {
    @synchronized(self) {
        if (toastLabel == nil) {
            toastLabel = [[UILabel alloc] init];
            toastLabel.backgroundColor = [UIColor darkGrayColor];
            toastLabel.font = [UIFont systemFontOfSize:16];
            toastLabel.textColor = [UIColor whiteColor];
            toastLabel.numberOfLines = 0;
            toastLabel.textAlignment = NSTextAlignmentCenter;
            toastLabel.lineBreakMode = NSLineBreakByCharWrapping;
            toastLabel.layer.masksToBounds = YES;
            toastLabel.layer.cornerRadius = 5.0;
            toastLabel.alpha = 0;
        }
        return toastLabel;
    }
}
//默认显示消息-->center
+ (void)showToastAction:(NSString *)message
{
    [self showToast:message location:@"center" showTime:2.0];
}
//显示消息
+ (void)showToast:(NSString *)message location:(NSString *)aLocationStr showTime:(float)aShowTime
{
    if (!message) {
        message = @"";
    }
    if ([[NSThread currentThread] isMainThread]) {
        toastLabel = [self currentToastLabel];
        [toastLabel removeFromSuperview];
        UIWindow *window = [UIApplication sharedApplication].delegate.window;
        [window addSubview:toastLabel];
        CGFloat main_width = [[UIScreen mainScreen] bounds].size.width;
        CGFloat main_height = [[UIScreen mainScreen] bounds].size.height;
        CGFloat width = [self stringText:message font:16 isHeightFixed:YES fixedValue:40];
        CGFloat height = 0;
        if (width > main_width - 20) {
            width = main_width - 20;
            height = [self stringText:message font:16 isHeightFixed:NO fixedValue:width];
        }else{
            height = 40;
        }
        CGRect labFrame;
        if (aLocationStr && [aLocationStr isEqualToString:@"top"]) {
            labFrame = CGRectMake((main_width-width)/2, main_height*0.15, width, height);
        }else if (aLocationStr && [aLocationStr isEqualToString:@"bottom"]) {
            labFrame = CGRectMake((main_width-width)/2, main_height*0.85, width, height);
        }else{
            //default-->center
            labFrame = CGRectMake((main_width-width)/2, main_height*0.5, width, height);
        }
        toastLabel.frame = labFrame;
        toastLabel.text = message;
        toastLabel.alpha = 1;
        [UIView animateWithDuration:aShowTime animations:^{
            toastLabel.alpha = 0;
        } completion:^(BOOL finished) {
        }];
    }else{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self showToast:message location:aLocationStr showTime:aShowTime];
        });
        return;
    }
}
//根据字符串长度获取对应的宽度或者高度
+ (CGFloat)stringText:(NSString *)aText font:(CGFloat)aFont isHeightFixed:(BOOL)isHeightFixed fixedValue:(CGFloat)fixedValue
{
    CGSize size;
    if (isHeightFixed) {
        size = CGSizeMake(MAXFLOAT, fixedValue);
    } else {
        size = CGSizeMake(fixedValue, MAXFLOAT);
    }
    CGSize resultSize = CGSizeZero;
    if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 7.0) {
        //返回计算出的size
        resultSize = [aText boundingRectWithSize:size options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:aFont]} context:nil].size;
    }
    if (isHeightFixed) {
        return resultSize.width + 20; //增加左右20间隔
    } else {
        return resultSize.height + 20; //增加上下20间隔
    }
}
@end
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnIntercomViewController.h
New file
@@ -0,0 +1,22 @@
//
//  ESOnIntercomViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnIntercomViewController : UIViewController
@property (nonatomic,strong) NSString               *mESVideoID;
@property (nonatomic,assign) int                    mESRoomID;//住户的房间ID
@property (nonatomic,strong) NSString               *roomName;
@property (nonatomic,strong) NSString               *deviceName;
@end
NS_ASSUME_NONNULL_END
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnIntercomViewController.m
New file
@@ -0,0 +1,1168 @@
//
//  ESOnIntercomViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import "ESOnIntercomViewController.h"
#import "ESOnUtlis.h"
#import <AVFoundation/AVFoundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
#import "AudioSessionHelper.h"
#import <Photos/Photos.h>
#import <AudioToolbox/AudioToolbox.h>
#import "ESVideo.h"
@interface ESOnIntercomViewController ()<ESVideoPhoneDelegate>
@property (nonatomic,strong) AudioSessionHelper    *sessionHelper;
@property (nonatomic,strong) ESVideoPhone          *es;
@property (nonatomic,assign) BOOL                  playing;
@property (nonatomic,assign) BOOL                  isInterrupt;
@property (nonatomic,assign) BOOL                  isSpeaking;
@property (nonatomic,strong) UIImage               *snapImage; //截图
@property (nonatomic, strong) UIButton *backButton;  //
@property (nonatomic, strong) UILabel *titleUILabel;  //
@property (nonatomic, strong) UIView *centerView;  //内容背景View
@property (nonatomic, strong) UIView *videoView;
@property (nonatomic, strong) UIView *unlockView;
@property (nonatomic, strong) UIButton *screenshotImgBtn;  //截图
@property (nonatomic, strong) UIButton *unlockImgBtn;  //开锁
@property (nonatomic, strong) UIButton *hangUpImgBtn;//挂断按钮
@property (nonatomic, strong) UIButton *hangUpTextBtn;
@property (nonatomic, strong) UIButton *answerImgBtn;//接听按钮
@property (nonatomic, strong) UIButton *answerTextBtn;
@property (nonatomic, strong) UIButton *calltimeBtn; //通话时间按钮
//定时器
@property (nonatomic,strong) dispatch_source_t countdownTimer;
@property (nonatomic,strong) dispatch_source_t openDoorTimer;
@property (nonatomic, assign) int openDoorTimeout;
@property (nonatomic, assign) int callTimeout;
@end
@implementation ESOnIntercomViewController{
    BOOL isAccessAudio;
    BOOL isAccessVideo;
    BOOL isBackGround;
    BOOL iSVideoNotDetermined;
    BOOL iSAudioNotDetermined;
    NSString * tipStr;
    NSString * okStr;
    NSString * saveToTheAlbumsStr;
    NSString * operationFailedStr;
    NSString * refuseStr;
    NSString * answerStr;
    NSString * unlockSuccessfullyStr;
    NSString * callingStr;
    NSString * hangUpStr;
    //    int openDoorTimeout;
    //全局变量
    SystemSoundID sound;
    CGFloat Height66;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.whiteColor;
    Height66 = GetRealWidth(66);
    [self initLlanguage];
    [self initTopBarView];
    [self initCentetView];
    [self initData];
    //    [self setAnswerBtnEnable:NO];
    [self initESVideo];
    //开始反呼
    [self StartReverseCall];
    [self ShowCalltimeBtn:callingStr];
    // Do any additional setup after loading the view.
}
-(void)initLlanguage{
    NSString *languageName = [[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0];
    // ç®€ä½“中文
    if ([languageName rangeOfString:@"zh-Hans"].location != NSNotFound) {
        tipStr = @"提示";
        okStr = @"确认";
        saveToTheAlbumsStr = @"已保存至手机相册.";
        operationFailedStr = @"操作失败";
        refuseStr = @"拒绝";
        answerStr = @"接听";
        hangUpStr = @"挂断";
        unlockSuccessfullyStr = @"开锁成功";
        callingStr = @"来电中...";
    }else{
        tipStr = @"Prompt";
        okStr = @"OK";
        saveToTheAlbumsStr = @"Saved to the albums.";
        operationFailedStr = @"Operation failed.";
        refuseStr = @"Refuse";
        answerStr = @"Answer";
        hangUpStr = @"Hang up";
        unlockSuccessfullyStr = @"Unlock successfully";
        callingStr = @"Incoming call";
    }
}
- (void)initTopBarView {
    UIView *TopView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, APP_TOP_BAR_HEIGHT)];
    TopView.backgroundColor = HEXCOLORA(0xF7F7F7,1.0);
    //    [TopView addSubview:self.backButton];
    [TopView addSubview:self.titleUILabel];
    [self.view addSubview:TopView];
    TopView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
    TopView.layer.shadowOffset = CGSizeMake(0,0.5);
    TopView.layer.shadowOpacity = 1;
    TopView.layer.shadowRadius = 0;
}
- (UIButton *)backButton{
    if (_backButton == nil) {
        _backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_STATUS_BAR_HEIGHT, 44, 44)];
        [_backButton setImage:[UIImage imageNamed:@"ic_esvideo_on_back"] forState:UIControlStateNormal];
        _backButton.imageEdgeInsets = UIEdgeInsetsMake(12,12,12,12);
        [_backButton.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    }
    return _backButton;
}
-(void)backAction{
    [self.navigationController popViewControllerAnimated:true];
    //    [self dismissViewControllerAnimated:YES completion:NULL];
}
-(UILabel *)titleUILabel{
    if (_titleUILabel == Nil) {
        _titleUILabel = [[UILabel alloc] initWithFrame:CGRectMake(90, APP_STATUS_BAR_HEIGHT, self.view.frame.size.width - 180, 44)];
        _titleUILabel.font = [UIFont fontWithName:APP_UIFont_BOLD size:17.0];
        _titleUILabel.textColor = HEXCOLORA(0X1B2D4D, 1.0);
        _titleUILabel.text = @"";
        _titleUILabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleUILabel;
}
- (void)initCentetView {
    [self.view addSubview:self.centerView];
    //    [self.centerView addSubview:self.collectButton];
    [self.centerView addSubview:self.videoView];
    [self.centerView addSubview:self.unlockView];
    [self.unlockView addSubview:self.screenshotImgBtn];
    [self.unlockView addSubview:self.unlockImgBtn];
    [self.centerView addSubview:self.hangUpImgBtn];
    [self.centerView addSubview:self.hangUpTextBtn];
    [self.centerView addSubview:self.answerImgBtn];
    [self.centerView addSubview:self.answerTextBtn];
    [self.centerView addSubview:self.calltimeBtn];
}
- (UIView *)centerView{
    if (_centerView == nil) {
        _centerView = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_TOP_BAR_HEIGHT, APP_SCREEN_WIDTH, APP_VISIBLE_HEIGHT)];
        _centerView.backgroundColor = UIColor.whiteColor;
    }
    return _centerView;
}
-(void)setRadiusWithView:(UIView *)mView{
    //顶部圆角
    UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
    if (@available(iOS 11.0, *)) {
        mView.layer.cornerRadius = 20;
        mView.layer.maskedCorners =  (CACornerMask)corners;
    }else{
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:mView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(20,20)];
        //创建 layer
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = mView.bounds;
        //赋值
        maskLayer.path = maskPath.CGPath;
        mView.layer.mask = maskLayer;
    }
}
-(UIView *)videoView{
    if (_videoView == Nil) {
        _videoView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, GetRealWidth(210))];
        _videoView.backgroundColor = UIColor.lightGrayColor;
        _videoView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
        _videoView.layer.shadowOffset = CGSizeMake(0,0.5);
        _videoView.layer.shadowOpacity = 1;
        _videoView.layer.shadowRadius = 0;
    }
    return _videoView;
}
- (UIView *)unlockView{
    if (_unlockView == nil) {
        _unlockView = [[UIView alloc] initWithFrame:CGRectMake(0, GetRealWidth(210), APP_SCREEN_WIDTH, GetRealWidth(72))];
        _unlockView.backgroundColor = HEXCOLORA(0x232323, 1.0);
        _unlockView.layer.backgroundColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0].CGColor;
        _unlockView.layer.shadowColor = [UIColor colorWithRed:204/255.0 green:204/255.0 blue:204/255.0 alpha:0.4].CGColor;
        _unlockView.layer.shadowOffset = CGSizeMake(0,0.5);
        _unlockView.layer.shadowOpacity = 1;
        _unlockView.layer.shadowRadius = 0;
    }
    return _unlockView;
}
- (UIButton *)screenshotImgBtn{
    if (_screenshotImgBtn == nil) {
        _screenshotImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(44), GetRealWidth(44))];
        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
        //        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateSelected];
        [_screenshotImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotImgBtn.adjustsImageWhenHighlighted = NO;
        _screenshotImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealWidth(38));
    }
    return _screenshotImgBtn;
}
-(void)screenshotDownAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateNormal];
}
-(void)screenshotAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
    //截图
    if(_es){
        [_es onSnap];
    }
}
- (UIButton *)unlockImgBtn{
    if (_unlockImgBtn == nil) {
        _unlockImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0,0, GetRealWidth(44), GetRealWidth(44))];
        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
        //        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateSelected];
        [_unlockImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_unlockImgBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_unlockImgBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockImgBtn.adjustsImageWhenHighlighted = NO;
        _unlockImgBtn.center = CGPointMake((APP_SCREEN_WIDTH/4) * 3, GetRealWidth(38));
    }
    return _unlockImgBtn;
}
-(void)unlockDownAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateNormal];
}
-(void)unlockAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
    //开锁
    if(_es){
        [_es openTheDoorWithRoomid:_mESRoomID];
    }
}
#pragma æŒ‚断和开锁
- (UIButton *)hangUpImgBtn{
    if (_hangUpImgBtn == nil) {
        _hangUpImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_hangUpImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_hangup"] forState:UIControlStateNormal];
        //        [_hangUpImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_hangup"] forState:UIControlStateSelected];
        [_hangUpImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_hangUpImgBtn addTarget:self action:@selector(hangUpAction) forControlEvents:UIControlEventTouchUpInside];
        _hangUpImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealHeight(452) + Height66/2);
    }
    return _hangUpImgBtn;
}
//挂断按钮事件
-(void)hangUpAction{
    [self backAction];
}
- (UIButton *)hangUpTextBtn{
    if (_hangUpTextBtn == nil) {
        _hangUpTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_hangUpTextBtn setTitle:@"拒绝" forState:UIControlStateNormal];
        _hangUpTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _hangUpTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_hangUpTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        [_hangUpTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_hangUpTextBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside];
        _hangUpTextBtn.center = CGPointMake(_hangUpImgBtn.center.x, GetRealHeight(530));
    }
    return _hangUpTextBtn;
}
- (UIButton *)answerImgBtn{
    if (_answerImgBtn == nil) {
        _answerImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_answerImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_answer"] forState:UIControlStateNormal];
        //        [_answerImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_answer"] forState:UIControlStateSelected];
        [_answerImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_answerImgBtn addTarget:self action:@selector(answerIAction) forControlEvents:UIControlEventTouchUpInside];
        _answerImgBtn.center = CGPointMake((APP_SCREEN_WIDTH/4)*3, GetRealHeight(452) + Height66/2);
    }
    return _answerImgBtn;
}
-(void)answerIAction{
    [self stopPlaySystemSound];
    if(_es){
        //        [self stopPlaySystemSound];
        [_es onAccept];
    }else{
        NSLog(@"ES初始化失败");
    }
    _answerImgBtn.hidden = YES;
    _answerTextBtn.hidden = YES;
    //挂断按钮移动中间
    _hangUpImgBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, _hangUpImgBtn.center.y);
    _hangUpTextBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, _hangUpTextBtn.center.y);
    [_hangUpTextBtn setTitle:hangUpStr forState:UIControlStateNormal];
    //开始计时
    _callTimeout = 0;
    [self startCountdown];
}
-(void)setAnswerBtnEnable:(BOOL)ISEnable{
    [_answerImgBtn setEnabled:ISEnable];
    [_answerTextBtn setEnabled:ISEnable];
}
- (UIButton *)answerTextBtn{
    if (_answerTextBtn == nil) {
        _answerTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_answerTextBtn setTitle:@"接听" forState:UIControlStateNormal];
        _answerTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        _answerTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        //        _unlockTextBtn.titleLabel.textColor = TextColor;
        [_answerTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        [_answerTextBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
        [_answerTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_answerTextBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside];
        _answerTextBtn.center = CGPointMake(_answerImgBtn.center.x, GetRealHeight(530));
    }
    return _answerTextBtn;
}
- (UIButton *)calltimeBtn{
    if (_calltimeBtn == nil) {
        _calltimeBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(40))];
        _calltimeBtn.backgroundColor = HEXCOLORA(0x000000, 0.6);
        [_calltimeBtn setTitle:@"00:00" forState:UIControlStateNormal];
        _calltimeBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:14.0];
        _calltimeBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        [_calltimeBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        _calltimeBtn.hidden = YES;
        _calltimeBtn.layer.cornerRadius = 12;
        _calltimeBtn.center = CGPointMake(APP_CONTENT_WIDTH/2, GetRealWidth(340));
    }
    return _calltimeBtn;
}
-(void)ShowTime:(int)nowTime {
    if(_calltimeBtn.hidden) _calltimeBtn.hidden = NO;
    NSString *timeStr = [self timeFormatted:nowTime];
    [self setCalltimeButtonText:timeStr isTime:YES];
}
-(void)ShowCalltimeBtn:(NSString*) mesStr  {
    if(_calltimeBtn.hidden) _calltimeBtn.hidden = NO;
    [self setCalltimeButtonText:mesStr isTime:NO];
}
/*
 æ ¹æ®æ–‡å­—调整按钮宽
 */
-(void)setCalltimeButtonText:(NSString*) mesStr isTime:(BOOL)isTime
{
    [_calltimeBtn setTitle:mesStr forState:UIControlStateNormal];
    if(isTime){
        _calltimeBtn.frame = CGRectMake(0, 0, GetRealWidth(80), GetRealWidth(30));
    }else{
        _calltimeBtn.frame = CGRectMake(0, 0, GetRealWidth(115), GetRealWidth(30));
    }
    _calltimeBtn.center = CGPointMake(APP_SCREEN_WIDTH/2, GetRealWidth(340));
}
- (NSString *)timeFormatted:(int)totalSeconds
{
    int seconds = totalSeconds % 60;
    int minutes = (totalSeconds / 60);
    return [NSString stringWithFormat:@"%02d:%02d", minutes, seconds];
}
- (int *)getTextWidth:(UIButton*) btn
{
    int textWidth = 0;
    //   Â  CGSize size = [btn.titleLabel.textsizeWithFont:[UIFontboldSystemFontOfSize:15]constrainedToSize:contentMaxSizes lineBreakMode:UILineBreakModeCharacterWrap];
    //    textWidth = (int)fontSize.Width;
    return textWidth;
}
/** å¼€å¯å€’计时 */
- (void)startCountdown {
    if (_callTimeout > 100) {
        return;
    }
    _callTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _countdownTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_countdownTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_countdownTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.callTimeout >= 100 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.countdownTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"超时");
                [weakSelf_AT backAction];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.callTimeout++;
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT ShowTime:weakSelf_AT.callTimeout];
            });
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_countdownTimer);
}
#pragma å¼€é”æˆåŠŸ
-(void)setOpenDoorSuccess{
    [self setUnlock:NO];
    _openDoorTimeout = 0;
    [self startOpenDoorCountdown];
    [self showUIAlertView:unlockSuccessfullyStr];
}
-(void)setUnlock:(BOOL)ISEnable{
    [_unlockImgBtn setEnabled:ISEnable];
}
/** å¼€å¯å€’计时 */
- (void)startOpenDoorCountdown {
    if (_openDoorTimeout > 20) {
        return;
    }
    _openDoorTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _openDoorTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_openDoorTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_openDoorTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.openDoorTimeout >= 20 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.openDoorTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT setUnlock:YES];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.openDoorTimeout++;
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_openDoorTimer);
}
#pragma SDK可视对讲 åŠŸèƒ½éƒ¨åˆ†
-(void)initESVideo{
    _es = ESVideo.shareInstance.es;
    //初始化中断,进入后台的tag
    _playing = NO;
    _isSpeaking = NO;
    self.isInterrupt = NO;
    isBackGround = NO;
    [self requestAccessForAVMediaType:AVMediaTypeAudio];
    if (isAccessAudio) {
        ImageCallback snapImageCallback = ^(UIImage *image){
            //block是在分线程中调用的,这里要放到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self->_snapImage = image;
                [self saveImageToPhotosAlbum:image];
            });
        };
        //门口机会有视频的长宽高,是固定的(暂时还不确定)
        //        _es = [[ESVideoPhone alloc]initESVideoPhoneWithFrame:CGRectMake(0, 57, APP_SCREEN_WIDTH, 211) delegate:self imagecallBack:snapImageCallback];
        if (_es) {
            //判断视频渲染是否初始化成功,如果失败会走ESVideoPhoneDelegate方法
            if (_es.showView) {
                ESVideo.shareInstance.snapImageCallback = snapImageCallback;
                _es.delegate = self;
                _es.showView.backgroundColor = [UIColor whiteColor];
                [self.videoView addSubview:_es.showView];
                _es.showView.hidden = YES;
            }
        }else{
            NSLog(@"ESVideoPhone åˆå§‹åŒ–失败");
            return;
        }
        // åˆå§‹åŒ–Audio采集Unit
        if(![_es initAudioCaptureSession]){
            return;
        }
    }else{
        //音频没有权限建议不要发起通话
        return;
    }
    //初始化视频采集Capture
//    [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    if (isAccessVideo) {
//        if(![_es initVideoCaptureSession]){
//            NSLog(@"VideoCaptureSession åˆå§‹åŒ–失败");
//        }
//    }
    //初始化AudioSession
    _sessionHelper = [[AudioSessionHelper alloc]init];
    [_sessionHelper setAudioSession];
    //添加进入后台,中断等通知
    [self addObservers];
}
-(void)initData{
    _titleUILabel.text = _deviceName;
    [_hangUpTextBtn setTitle:refuseStr forState:UIControlStateNormal];
    [_answerTextBtn setTitle:answerStr forState:UIControlStateNormal];
}
/**
 å¼€å§‹åå‘¼
 */
-(void)StartReverseCall{
    [self startPlaySystemSound];
    if(_es){
        NSString *param = [NSString stringWithFormat:@"address=%@,tag=mobile://123,",_mESVideoID];
        //NSLog(@"%@", param);
        [_es onReverseCall:param];
    }else{
        NSLog(@"ES初始化失败");
    }
}
////接听
//-(void)onAccept{
//    if(_es){
//        [_es onAccept];
//    }else{
//        NSLog(@"ES初始化失败");
//    }
//
//}
-(NSString *)getCurrentdateInterval
{
    NSDate *datenow = [NSDate date];
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)([datenow timeIntervalSince1970]*1000)];
    return timeSp;
}
-(void)showUIAlertView:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alertController animated:YES completion:nil];
    //    UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:tipStr message:mes delegate:self cancelButtonTitle:okStr otherButtonTitles:nil, nil];
    //    [alertView1 show];
}
-(void)showUIAlertViewWithBack:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [self backAction];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
    //这个方法请根据App的具体情况调用
    //在viewDidLoad中 è°ƒç”¨requestAccessForAVMediaType: æ˜¯ä¸ºäº†èŠ‚çº¦åˆå§‹åŒ–çš„æ—¶é—´
    //在viewDidAppear中调用requestAccessForAVMediaType: æ˜¯ä¸ºäº†å¼¹å‡ºæç¤ºæ‰“开权限的Alert
    //测试的时候发现如下情况:如果只把授权方法放到ViewDidAppear方法中处理,如果没有授权在初始化采集器时会失败。同样AlertView会因为View没有didLoad而导致present不出来
//    if (!isAccessVideo || !isAccessAudio) {
//        [self requestAccessForAVMedia];
//    }
//
    if (!isAccessAudio) {
        [self requestAccessForAVMedia];
    }
}
-(void)setIsInterrupt:(BOOL)isInterrupt{
    if (_es) {
        _es.isInterrupt = isInterrupt;
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self stopPlaySystemSound];
    //防止用户不按挂断,或者不等收到对方的挂断,点击返回按钮。
    if(_es){
        [_es onHangup];
        [_es onStopCapture];
        [_es stopTalk];
        _es.delegate = nil;
        ESVideo.shareInstance.snapImageCallback = nil;
    }
    //    [_es freeSubClass];
}
-(void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    //    [_es freeSubClass];
    if(_openDoorTimer){
        dispatch_source_cancel(_openDoorTimer);
        _openDoorTimer = nil; // OK
    }
    if(_countdownTimer){
        dispatch_source_cancel(_countdownTimer);
        _countdownTimer = nil; // OK
    }
    NSLog(@"==============dealloc");
}
#pragma mark ESVideoPhoneDelegate
//视频通话的状态代理事件,phoneEvent为返回的消息里面包含event状态与与event相关的数据
-(void)getPhoneEvent_UI:(NSString *)phoneEvent{
    NSLog(@"事件%@", phoneEvent);
    NSArray *strArray = [phoneEvent componentsSeparatedByString:@"\r\n"];
    NSArray *eventArray = [strArray.firstObject componentsSeparatedByString:@"="];
    NSString *phoneEventStr = eventArray.lastObject;
    if([phoneEventStr isEqual:@"EVT_Ringing"]){
        _es.showView.hidden = NO;
        //反呼成功 å…è®¸æŽ¥å¬  çœ‹éœ€æ±‚是否需要播放铃声和震动
        [self setAnswerBtnEnable:YES];
        //           [_mCallOrAccept setTitle:@"接听" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_StartStream"]){
    } else if([phoneEventStr isEqual:@"EVT_StopStream"]){
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_Connected"]){
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_HangUp"]){
        [self showUIAlertViewWithBack:@"已挂断"];
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_P2POnlineStatusChanged"]){
        //EVT_P2PStarted(p2p初始化OK,可以连接),EVT_P2POnlineStatusChangedonline=1
        //p2p初始化成功,手机端目前没有这个回调了
        //_mCallOrAccept.enabled = YES;
        //_monitorBtn.enabled = YES;
    }else if([phoneEventStr  isEqual:@"EVT_RECV_CUSTOM_DATA"]){
        //开门的结果从这里返回
        NSString *baseStr = [strArray[1] substringFromIndex:5];
        NSData *data = [[NSData alloc]initWithBase64EncodedString:baseStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSInteger status = [[dic valueForKey:@"status"]integerValue];
        if(status && status == 1){
            NSLog(@"开门成功");
            [self setOpenDoorSuccess];
        }else{
            NSLog(@"开门失败");
        }
    }
}
-(void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger)errortype{
    NSLog(@"错误%@", errorStr);
    //没有授权
    if (errortype == LMPVideoCaptureErrorNotAuthorized) {
        NSLog(@"错误%@", errorStr);
    }
}
#pragma mark AudioSession与Notifications处理
- (void) addObservers
{
    //    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name: UIKeyboardWillChangeFrameNotification  object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
    //isAccessVideo,如果AVCaptureSession没有new出来不会收到通知
    if (isAccessVideo) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:nil];
    }else{
        //object:为nil å¯èƒ½ä¸ä¼šè§¦å‘通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification object:[AVAudioSession
                                                                                                         sharedInstance]];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) sessionRuntimeError:(NSNotification*)notification
{
    NSError* error = notification.userInfo[AVCaptureSessionErrorKey];
    NSLog(@"Capture session runtime error: %@", error);
    // If media services were reset, and the last start succeeded, restart the session.
    if (error.code == AVErrorMediaServicesWereReset) {
        [_es onStopCapture];
        [_es startTalk];
    }
}
- (void)handleInterruption:(NSNotification *)notification
{
    NSUInteger interruptionType = [[[notification userInfo]
                                    objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
        if (isBackGround) {
            return;
        }
        [_es stopTalk];
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
        if (self.isInterrupt == NO) {
            return;
        }else{
            //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
            [self InterruptionEndedAVAudioSessionSetActiveYES];
        }
    }
}
//AVAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
- (void) sessionWasInterrupted:(NSNotification*)notification
{
    if (_playing == YES) {
        self.isInterrupt = YES;
        //AVCaptureSessionInterruptionReason
        if (@available(iOS 9.0, *)) {
            NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; //电话中断是1
            NSLog(@"Capture session was interrupted with reason %ld", (long)reason);
            //音频硬件暂时不可用而造成的中断,例如,电话或警报。
            if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
                reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient");
                //VAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
                //Audio Queue Services, I/O audio unit
                [_es onStopCapture];
                [_es stopTalk];
                /*
                 NSError *error = nil;
                 [[AVAudioSession sharedInstance] setActive:NO error:&error];
                 if (error) {
                 NSLog(@"sessionWasInterruptedSetActiveNO error:%@", error);
                 }
                 */
            }else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground){
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground");
                //如果是电话中断,不会走进入后台的通知,进入后台再切换到前台这里是不用处理的
                if (isBackGround) {
                    return;
                }
                [_es onStopCapture];
                [_es stopTalk];
            }
            //多个应用程序资源争用质量下降。只有当应用程序占据全屏时,会话才能运行。
            else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps");
                // Fade-in a label to inform the user that the camera is unavailable.
            }else if (@available(iOS 11.1, *)) {
                if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure){
                    NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure");
                }
            } else {
                // Fallback on earlier versions
            }
        } else {
            if (isBackGround) {
                return;
            }
            [_es onStopCapture];
            [_es stopTalk];
        }
    }
}
//这个通知可能会获取不到,
- (void) sessionInterruptionEnded:(NSNotification*)notification
{
    //    NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
    NSLog(@"Capture session interruption ended");
    if (self.isInterrupt == NO) {
        return;
    }else{
        //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
        [self InterruptionEndedAVAudioSessionSetActiveYES];
    }
}
-(void)InterruptionEndedAVAudioSessionSetActiveYES{
    if (isBackGround) {
        return;
    }
    if (self.isInterrupt == YES) {
        [_es onStartCapture];
        [_es startTalk];
        self.isInterrupt = NO;
    }
}
- (void)speaker:(UIButton *)sender {
    [_es stopTalk];
    NSString *result = nil;
    //听筒状态 æ’耳塞后拔掉后恢复到默认设置
    if (sender == nil) {
        result = [_sessionHelper speaker:NO];
    }else{
        if(!_isSpeaking){
            result = [_sessionHelper speaker:YES];
            _isSpeaking = YES;
        }else{
            result = [_sessionHelper speaker:NO];
            _isSpeaking = NO;
        }
    }
    if (result) {
        [sender setTitle:result forState:UIControlStateNormal];
        [_es startTalk];
    }
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            //免提状态下耳机插入没有采集,同意切换到默认状态
            NSLog(@"耳机插入");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"耳机拔出");
            //            if([[_speakerBtn titleForState:UIControlStateNormal] isEqualToString:@"免提"]){
            //                 [self speaker:nil];
            //            }else{
            //                [self speaker:_speakerBtn];
            //            }
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            //NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
/*
 éœ€è¦æ³¨æ„çš„æ˜¯ï¼Œæœ‰ä¸€ä¸ªä¸­æ–­å¼€å§‹æ¶ˆæ¯ä¸ä¸€å®šä¼šæœ‰ä¸€ä¸ªä¸­æ–­ç»“束消息,这就意味着中断结束的回调里的处理逻辑可能会没有被执行到。
 æ‰€ä»¥éœ€è¦å…³æ³¨å½“切到前台运行状态时,是不是需要重新激活你的 Audio Session。
 */
- (void)willEnterForeground:(NSNotification*)notification{
    NSLog(@"willEnterForeground");
    //初次启动会走这个通知(根页面),这时候是没有进入后台的
    if (isBackGround) {
        return;
    }
    [self InterruptionEndedAVAudioSessionSetActiveYES];
    // è¿™é‡Œæ˜¯è€ƒè™‘到用户没有授权,之后通过AlertAction跳转到设置页面授权后再回到APP时做的重新检测
    //跳转到设置页面,授权后返回页面,继续初始化采集器
    if(!isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
    }
//    if (isAccessAudio && isAccessVideo) {
//        return;
//    }
//    if (isAccessVideo && !isAccessAudio) {
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
//    }else if (!isAccessVideo && isAccessAudio){
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//
//    }else if (!isAccessVideo && !isAccessAudio){
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//    }
}
- (void)willEnterBackground:(NSNotification *)notification {
    isBackGround = YES;
}
//授权Alert
-(void)requestAccessForAVMedia{
    if (!isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
    }
//    if (!isAccessVideo) {
//        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    }
    if (!iSAudioNotDetermined && iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"相机没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(!iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风与相机授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }
}
-(void)requestAccessForAVMediaType:(AVMediaType)type{
    if (type == AVMediaTypeVideo) {
        isAccessVideo = YES;
        iSVideoNotDetermined = YES;
    }else{
        isAccessAudio = YES;
        iSAudioNotDetermined = YES;
    }
    switch ([AVCaptureDevice authorizationStatusForMediaType:type])
    {
        case AVAuthorizationStatusAuthorized:
        {
            break;
        }
        case AVAuthorizationStatusNotDetermined:
        {
            dispatch_suspend(dispatch_get_main_queue());
            [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) {
                if (!granted) {
                    if (type == AVMediaTypeVideo) {
                        self->isAccessVideo = NO;
                    }else{
                        self->isAccessAudio = NO;
                    }
                }
                dispatch_resume(dispatch_get_main_queue());
            }];
            break;
        }
        default:
        {
            if (type == AVMediaTypeVideo) {
                isAccessVideo = NO;
                iSVideoNotDetermined = NO;
            }else{
                isAccessAudio = NO;
                iSAudioNotDetermined = NO;
            }
            break;
        }
    }
}
-(void)creatAlertViewWith:(NSString *)title message:(NSString *) msg cancel:(NSString *)cancelMsg{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:cancelMsg style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma ä¿å­˜å›¾ç‰‡åˆ°ç›¸å†Œ
- (void)saveImageToPhotosAlbum:(UIImage *)savedImage
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined)
    {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if(status == PHAuthorizationStatusAuthorized)
            {
                UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    }
    else
    {
        if (status == PHAuthorizationStatusAuthorized)
        {
            UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}
// æŒ‡å®šå›žè°ƒæ–¹æ³•
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *message = nil;
    if (!error) {
        message = saveToTheAlbumsStr;
    }
    else
    {
        message = operationFailedStr;
    }
    [self showUIAlertView:message];
}
#pragma éœ‡åŠ¨å®žçŽ°è²Œä¼¼å’ŒSDK冲突 ä¸èƒ½å®žçŽ°éœ‡åŠ¨
//开始播放的时候调用
-(void)startPlaySystemSound{
    return;
    //    //震动的提示文件名放到资源目录下
    //    NSString *path = [[NSBundle mainBundle] pathForResource:@"ring" ofType:@"wav"];
    //    AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &sound);
    //分别注册铃声和震动完后的回调
    AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, NULL, NULL, vibrationCompleteCallback, NULL);
    //    AudioServicesAddSystemSoundCompletion(sound, NULL, NULL, soundCompleteCallback, NULL);
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);//开始震动
    //    AudioServicesPlaySystemSound(sound);//开始播放铃声
}
//手动停止播放的时候调用
- (void)stopPlaySystemSound{
    return;
    NSLog(@"stop PlaySystemSound");
    stopRingAndVibration();
}
//停止响铃和震动,移除回调并处理掉铃声和震动
void stopRingAndVibration() {
    AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate);
    //    AudioServicesRemoveSystemSoundCompletion(sound);
    AudioServicesDisposeSystemSoundID(kSystemSoundID_Vibrate);
    //    AudioServicesDisposeSystemSoundID(sound);
}
//震动完成回调,因为震动一下便会调用一次,这里延迟800ms再继续震动,和微信差不多,时间长短可自己控制。参数sound即为注册回调时传的第一个参数
void vibrationCompleteCallback(SystemSoundID sound,void * clientData) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(800 * NSEC_PER_MSEC)), dispatch_get_global_queue(0, 0), ^{
        AudioServicesPlaySystemSound(sound);
    });
}
////铃声播放完成回调,这种方法x播放的音频限制在30秒内,播放完直接响铃和震动
//void soundCompleteCallback(SystemSoundID sound,void * clientData) {
//
//    stopRingAndVibration();
//}
@end
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnMonitorViewController.h
New file
@@ -0,0 +1,23 @@
//
//  ESOnMonitorViewController.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnMonitorViewController : UIViewController
@property (nonatomic,strong) NSString               *mESVideoID;
@property (nonatomic,assign) int                    mESRoomID;//住户的房间ID
@property (nonatomic,strong) NSString               *roomName;
@property (nonatomic,strong) NSString               *deviceName;
@end
NS_ASSUME_NONNULL_END
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnMonitorViewController.m
New file
@@ -0,0 +1,929 @@
//
//  ESOnMonitorViewController.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#import "ESOnMonitorViewController.h"
#import "ESOnUtlis.h"
#import <AVFoundation/AVFoundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
#import <Photos/Photos.h>
#import "AudioSessionHelper.h"
//#import "CBToast.h"
#import "ESVideo.h"
@interface ESOnMonitorViewController ()<ESVideoPhoneDelegate>
@property (nonatomic,strong) AudioSessionHelper    *sessionHelper;
@property (nonatomic,strong) ESVideoPhone          *es;
@property (nonatomic,assign) BOOL                  playing;
@property (nonatomic,assign) BOOL                  isInterrupt;
@property (nonatomic,assign) BOOL                  isSpeaking;
@property (nonatomic,strong) UIImage               *snapImage; //截图
@property (nonatomic, strong) UIButton *backButton;  //
@property (nonatomic, strong) UILabel *titleUILabel;  //
@property (nonatomic, strong) UIView *centerView;  //内容背景View
@property (nonatomic, strong) UIView *videoView;
@property (nonatomic, strong) UIButton *screenshotImgBtn;  //
@property (nonatomic, strong) UIButton *screenshotTextBtn;  //
@property (nonatomic, strong) UIButton *unlockImgBtn;  //
@property (nonatomic, strong) UIButton *unlockTextBtn;  //
@property (nonatomic,strong) dispatch_source_t openDoorTimer;
@property (nonatomic, assign) int openDoorTimeout;
@end
@implementation ESOnMonitorViewController
{
    BOOL isAccessAudio;
    BOOL isAccessVideo;
    BOOL isBackGround;
    BOOL iSVideoNotDetermined;
    BOOL iSAudioNotDetermined;
    NSString * tipStr;
    NSString * okStr;
    NSString * saveToTheAlbumsStr;
    NSString * operationFailedStr;
    NSString * screenshotStr;
    NSString * unlockStr;
    NSString * endOfMonitoringStr;
    NSString * unlockSuccessfullyStr;
    CGFloat Height66;
    //    int _openDoorTimeout;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.whiteColor;
    Height66 = GetRealWidth(66);
    [self initLlanguage];
    [self initTopBarView];
    [self initCentetView];
    [self initData];
    [self initESVideo];
    [self StartMonitoring];
    // Do any additional setup after loading the view.
}
-(void)initLlanguage{
    NSString *languageName = [[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"] objectAtIndex:0];
    // ç®€ä½“中文
    if ([languageName rangeOfString:@"zh-Hans"].location != NSNotFound) {
        tipStr = @"提示";
        okStr = @"确认";
        saveToTheAlbumsStr = @"已保存至手机相册.";
        operationFailedStr = @"操作失败";
        screenshotStr = @"截图";
        unlockStr = @"开锁";
        endOfMonitoringStr = @"监视结束";
        unlockSuccessfullyStr = @"开锁成功";
    }else{
        tipStr = @"Prompt";
        okStr = @"OK";
        saveToTheAlbumsStr = @"Saved to the albums.";
        operationFailedStr = @"Operation failed.";
        screenshotStr = @"Screenshot";
        unlockStr = @"Unlock";
        endOfMonitoringStr = @"End of monitoring";
        unlockSuccessfullyStr = @"Unlock successfully";
    }
}
//顶部View
- (void)initTopBarView {
    UIView *TopView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, APP_TOP_BAR_HEIGHT)];
    TopView.backgroundColor = HEXCOLORA(0xF7F7F7,1.0);
    [TopView addSubview:self.backButton];
    [TopView addSubview:self.titleUILabel];
//    [TopView addSubview:self.moreButton];
    [self.view addSubview:TopView];
    TopView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
    TopView.layer.shadowOffset = CGSizeMake(0,0.5);
    TopView.layer.shadowOpacity = 1;
    TopView.layer.shadowRadius = 0;
}
- (UIButton *)backButton{
    if (_backButton == nil) {
        _backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_STATUS_BAR_HEIGHT, 44, 44)];
        [_backButton setImage:[UIImage imageNamed:@"ic_esvideo_on_back"] forState:UIControlStateNormal];
        _backButton.imageEdgeInsets = UIEdgeInsetsMake(12,12,12,12);
        [_backButton.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    }
    return _backButton;
}
-(void)backAction{
    [self.navigationController popViewControllerAnimated:true];
    //    [self dismissViewControllerAnimated:YES completion:NULL];
}
-(UILabel *)titleUILabel{
    if (_titleUILabel == Nil) {
        _titleUILabel = [[UILabel alloc] initWithFrame:CGRectMake(90, APP_STATUS_BAR_HEIGHT, self.view.frame.size.width - 180, 44)];
        _titleUILabel.font = [UIFont fontWithName:APP_UIFont_BOLD size:17.0];
        _titleUILabel.textColor = HEXCOLORA(0X1B2D4D, 1.0);
        _titleUILabel.text = @"";
        _titleUILabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleUILabel;
}
- (void)initCentetView {
    [self.view addSubview:self.centerView];
    //    [self.centerView addSubview:self.collectButton];
//    [self.centerView addSubview:self.homeView];
    [self.centerView addSubview:self.videoView];
    [self.centerView addSubview:self.screenshotImgBtn];
    [self.centerView addSubview:self.screenshotTextBtn];
    [self.centerView addSubview:self.unlockImgBtn];
    [self.centerView addSubview:self.unlockTextBtn];
}
- (UIView *)centerView{
    if (_centerView == nil) {
        _centerView = [[UIButton alloc] initWithFrame:CGRectMake(0, APP_TOP_BAR_HEIGHT, APP_SCREEN_WIDTH, APP_VISIBLE_HEIGHT)];
        _centerView.backgroundColor = UIColor.whiteColor;
//        [self setRadiusWithView:_centerView];
    }
    return _centerView;
}
-(void)setRadiusWithView:(UIView *)mView{
    //顶部圆角
    UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
    if (@available(iOS 11.0, *)) {
        mView.layer.cornerRadius = 20;
        mView.layer.maskedCorners =  (CACornerMask)corners;
    }else{
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:mView.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(20,20)];
        //创建 layer
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = mView.bounds;
        //赋值
        maskLayer.path = maskPath.CGPath;
        mView.layer.mask = maskLayer;
    }
}
-(UIView *)videoView{
    if (_videoView == Nil) {
        _videoView =  [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, GetRealWidth((210)))];
        _videoView.backgroundColor = UIColor.lightGrayColor;
        _videoView.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.25].CGColor;
        _videoView.layer.shadowOffset = CGSizeMake(0,0.5);
        _videoView.layer.shadowOpacity = 1;
        _videoView.layer.shadowRadius = 0;
    }
    return _videoView;
}
#pragma æˆªå›¾å›¾æ ‡æŒ‰é’®
- (UIButton *)screenshotImgBtn{
    if (_screenshotImgBtn == nil) {
        _screenshotImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
        //        [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateSelected];
        [_screenshotImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotImgBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotImgBtn.adjustsImageWhenHighlighted = NO;
        _screenshotImgBtn.center = CGPointMake(APP_SCREEN_WIDTH/4, GetRealWidth(276) + Height66/2);
    }
    return _screenshotImgBtn;
}
-(void)screenshotDownAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_select"] forState:UIControlStateNormal];
    [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateNormal];
}
-(void)screenshotAction{
    [_screenshotImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_takephoto_unselect"] forState:UIControlStateNormal];
    [_screenshotTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
    //截图
    if(_es){
        [_es onSnap];
    }
}
#pragma æˆªå›¾æ–‡å­—按钮
- (UIButton *)screenshotTextBtn{
    if (_screenshotTextBtn == nil) {
        _screenshotTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_screenshotTextBtn setTitle:@"截图" forState:UIControlStateNormal];
        _screenshotTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        _screenshotTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        [_screenshotTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        //        [_screenshotTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_screenshotTextBtn addTarget:self action:@selector(screenshotAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_screenshotTextBtn addTarget:self action:@selector(screenshotDownAction) forControlEvents:UIControlEventTouchDown];
        _screenshotTextBtn.adjustsImageWhenHighlighted = NO;
        _screenshotTextBtn.center = CGPointMake(_screenshotImgBtn.center.x, GetRealWidth(364));
    }
    return _screenshotTextBtn;
}
- (UIButton *)unlockImgBtn{
    if (_unlockImgBtn == nil) {
        _unlockImgBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, Height66, Height66)];
        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
        //        [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateSelected];
        [_unlockImgBtn.imageView setContentMode:UIViewContentModeScaleAspectFit];
        [_unlockImgBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
        [_unlockImgBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockImgBtn.adjustsImageWhenHighlighted = NO;
        _unlockImgBtn.center = CGPointMake((APP_SCREEN_WIDTH / 4) * 3, GetRealWidth(276) + Height66/2);
    }
    return _unlockImgBtn;
}
-(void)unlockDownAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_select"] forState:UIControlStateNormal];
    [_unlockTextBtn setTitleColor:TextSelectColor forState:UIControlStateNormal];
}
-(void)unlockAction{
    [_unlockImgBtn setImage:[UIImage imageNamed:@"ic_esvideo_on_unlock_unselect"] forState:UIControlStateNormal];
    [_unlockTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
    //开锁
    if(_es){
        [_es openTheDoorWithRoomid:_mESRoomID];
    }
}
- (UIButton *)unlockTextBtn{
    if (_unlockTextBtn == nil) {
        _unlockTextBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, GetRealWidth(100), GetRealWidth(20))];
        [_unlockTextBtn setTitle:@"开锁" forState:UIControlStateNormal];
        _unlockTextBtn.titleLabel.font = [UIFont fontWithName:APP_UIFont size:15.0];
        _unlockTextBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
        //        _unlockTextBtn.titleLabel.textColor = TextColor;
        [_unlockTextBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
        [_unlockTextBtn setTitleColor:TextColor forState:UIControlStateNormal];
        //        [_unlockTextBtn setTitleColor:TextSelectColor forState:UIControlStateSelected];
        [_unlockTextBtn addTarget:self action:@selector(unlockAction) forControlEvents:UIControlEventTouchUpInside];
        [_unlockTextBtn addTarget:self action:@selector(unlockDownAction) forControlEvents:UIControlEventTouchDown];
        _unlockTextBtn.adjustsImageWhenHighlighted = NO;
        _unlockTextBtn.center = CGPointMake(_unlockImgBtn.center.x, GetRealWidth(364));
    }
    return _unlockTextBtn;
}
#pragma å¼€é”æˆåŠŸ
-(void)setOpenDoorSuccess{
    [self setUnlock:NO];
    _openDoorTimeout = 0;
    [self startOpenDoorCountdown];
    [self showUIAlertView:unlockSuccessfullyStr];
}
-(void)setUnlock:(BOOL)ISEnable{
    [_unlockImgBtn setEnabled:ISEnable];
    [_unlockTextBtn setEnabled:ISEnable];
}
/** å¼€å¯å€’计时 */
- (void)startOpenDoorCountdown {
    if (_openDoorTimeout > 20) {
        return;
    }
    _openDoorTimeout = 0;
    // GCD定时器
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _openDoorTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_openDoorTimer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0); //每秒执行
    dispatch_source_set_event_handler(_openDoorTimer, ^{
        WEAKSELF_AT
        if(weakSelf_AT.openDoorTimeout >= 20 ){// è®¡æ—¶ç»“束
            // å…³é—­å®šæ—¶å™¨
            dispatch_source_cancel(weakSelf_AT.openDoorTimer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf_AT setUnlock:YES];
            });
        }else{// è®¡æ—¶ä¸­
            weakSelf_AT.openDoorTimeout++;
        }
    });
    // å¼€å¯å®šæ—¶å™¨
    dispatch_resume(_openDoorTimer);
}
#pragma SDK可视对讲 åŠŸèƒ½éƒ¨åˆ†
-(void)initESVideo{
    //初始化中断,进入后台的tag
    _es = ESVideo.shareInstance.es;
    _playing = NO;
    _isSpeaking = NO;
    self.isInterrupt = NO;
    isBackGround = NO;
    [self requestAccessForAVMediaType:AVMediaTypeAudio];
    if (isAccessAudio) {
        ImageCallback snapImageCallback = ^(UIImage *image){
            //block是在分线程中调用的,这里要放到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self->_snapImage = image;
                [self saveImageToPhotosAlbum:image];
            });
        };
        //门口机会有视频的长宽高,是固定的(暂时还不确定)
        if (_es) {
            //判断视频渲染是否初始化成功,如果失败会走ESVideoPhoneDelegate方法
            if (_es.showView) {
                _es.delegate = self;
                ESVideo.shareInstance.snapImageCallback = snapImageCallback;
                _es.showView.backgroundColor = [UIColor whiteColor];
                [self.videoView addSubview:_es.showView];
                _es.showView.hidden = YES;
            }
        }else{
            NSLog(@"ESVideoPhone åˆå§‹åŒ–失败");
            return;
        }
        // åˆå§‹åŒ–Audio采集Unit
        if(![_es initAudioCaptureSession]){
            return;
        }
    }else{
        //音频没有权限建议不要发起通话
        return;
    }
    //初始化视频采集Capture
//    [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    if (isAccessVideo) {
//        if(![_es initVideoCaptureSession]){
//            NSLog(@"VideoCaptureSession åˆå§‹åŒ–失败");
//        }
//    }
    //初始化AudioSession
    _sessionHelper = [[AudioSessionHelper alloc]init];
    [_sessionHelper setAudioSession];
    //添加进入后台,中断等通知
    [self addObservers];
}
-(void)initData{
//    _topUILabel.text = _deviceName;
//    _homeUILabel.text = _roomName;
    [_screenshotTextBtn setTitle:screenshotStr forState:UIControlStateNormal];
    [_unlockTextBtn setTitle:unlockStr forState:UIControlStateNormal];
    _titleUILabel.text = _deviceName;
//    [_collectButton setSelected:_isCollect];
}
/**
 å¼€å§‹ç›‘控
 */
-(void)StartMonitoring{
    if(_es){
        [_es onMonitor:_mESVideoID];
    }else{
        NSLog(@"ES初始化失败");
    }
}
-(NSString *)getCurrentdateInterval
{
    NSDate *datenow = [NSDate date];
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)([datenow timeIntervalSince1970]*1000)];
    return timeSp;
}
-(void)showUIAlertView:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alertController animated:YES completion:nil];
}
-(void)showUIAlertViewWithBack:(NSString *)mes
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:tipStr message:mes preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:okStr style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [self backAction];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma viewDidAppear
-(void)viewDidAppear:(BOOL)animated{
    //这个方法请根据App的具体情况调用
    //在viewDidLoad中 è°ƒç”¨requestAccessForAVMediaType: æ˜¯ä¸ºäº†èŠ‚çº¦åˆå§‹åŒ–çš„æ—¶é—´
    //在viewDidAppear中调用requestAccessForAVMediaType: æ˜¯ä¸ºäº†å¼¹å‡ºæç¤ºæ‰“开权限的Alert
    //测试的时候发现如下情况:如果只把授权方法放到ViewDidAppear方法中处理,如果没有授权在初始化采集器时会失败。同样AlertView会因为View没有didLoad而导致present不出来
//    if (!isAccessVideo || !isAccessAudio) {
//        [self requestAccessForAVMedia];
//    }
    if (!isAccessAudio) {
        [self requestAccessForAVMedia];
    }
}
-(void)setIsInterrupt:(BOOL)isInterrupt{
    if (_es) {
        _es.isInterrupt = isInterrupt;
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    //防止用户不按挂断,或者不等收到对方的挂断,点击返回按钮。
    if(_es){
        [_es onHangup];
        [_es onStopCapture];
        [_es stopTalk];
        _es.delegate = nil;
        ESVideo.shareInstance.snapImageCallback = nil;
        //        _es.showView.removeFromSuperview;
    }
    //    [_es freeSubClass];
}
-(void)dealloc{
    NSLog(@"==============dealloc  1");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    NSLog(@"==============dealloc  2");
    //    [_es freeSubClass];
    //     NSLog(@"==============dealloc  3");
    if(_openDoorTimer){
        dispatch_source_cancel(_openDoorTimer);
        _openDoorTimer = nil; // OK
    }
    NSLog(@"==============dealloc 3");
}
#pragma mark ESVideoPhoneDelegate
//视频通话的状态代理事件,phoneEvent为返回的消息里面包含event状态与与event相关的数据
-(void)getPhoneEvent_UI:(NSString *)phoneEvent{
    NSLog(@"==============ESVideoPhoneDelegate事件%@", phoneEvent);
    NSArray *strArray = [phoneEvent componentsSeparatedByString:@"\r\n"];
    NSArray *eventArray = [strArray.firstObject componentsSeparatedByString:@"="];
    NSString *phoneEventStr = eventArray.lastObject;
    if([phoneEventStr isEqual:@"EVT_Ringing"]){
        //           [_mCallOrAccept setTitle:@"接听" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_StartStream"]){
    } else if([phoneEventStr isEqual:@"EVT_StopStream"]){
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_Connected"]){
        [self speaker:nil];
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr isEqual:@"EVT_MonitorConnected"]){
        _es.showView.hidden = NO;
        [self speaker:nil];
        //           [_mCallOrAccept setTitle:@"通话中..." forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_HangUp"]){
        //        [CBToast showToastAction:endOfMonitoringStr];
        [self showUIAlertViewWithBack:endOfMonitoringStr];
        //        [self backAction];
        //           [_mCallOrAccept setTitle:@"反呼" forState:UIControlStateNormal];
    }else if([phoneEventStr  isEqual:@"EVT_P2POnlineStatusChanged"]){
        //EVT_P2PStarted(p2p初始化OK,可以连接),EVT_P2POnlineStatusChangedonline=1
        //p2p初始化成功,手机端目前没有这个回调了
        //_mCallOrAccept.enabled = YES;
        //_monitorBtn.enabled = YES;
    }else if([phoneEventStr  isEqual:@"EVT_RECV_CUSTOM_DATA"]){
        //开门的结果从这里返回
        NSString *baseStr = [strArray[1] substringFromIndex:5];
        NSData *data = [[NSData alloc]initWithBase64EncodedString:baseStr options:NSDataBase64DecodingIgnoreUnknownCharacters];
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSInteger status = [[dic valueForKey:@"status"]integerValue];
        if(status && status == 1){
            NSLog(@"开门成功");
            [self setOpenDoorSuccess];
        }else{
            NSLog(@"开门失败");
        }
    }
}
-(void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger)errortype{
    NSLog(@"==============ErrorForESVideo错误%@", errorStr);
    //没有授权
    if (errortype == LMPVideoCaptureErrorNotAuthorized) {
        NSLog(@"错误%@", errorStr);
    }
}
#pragma mark AudioSession与Notifications处理
- (void) addObservers
{
    //    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name: UIKeyboardWillChangeFrameNotification  object: nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:nil];
    //isAccessVideo,如果AVCaptureSession没有new出来不会收到通知
    if (isAccessVideo) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionWasInterrupted:) name:AVCaptureSessionWasInterruptedNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionInterruptionEnded:) name:AVCaptureSessionInterruptionEndedNotification object:nil];
    }else{
        //object:为nil å¯èƒ½ä¸ä¼šè§¦å‘通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:)
                                                     name:AVAudioSessionInterruptionNotification object:[AVAudioSession
                                                                                                         sharedInstance]];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:)   name:AVAudioSessionRouteChangeNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
- (void) sessionRuntimeError:(NSNotification*)notification
{
    NSError* error = notification.userInfo[AVCaptureSessionErrorKey];
    NSLog(@"Capture session runtime error: %@", error);
    // If media services were reset, and the last start succeeded, restart the session.
    if (error.code == AVErrorMediaServicesWereReset) {
        [_es onStopCapture];
        [_es startTalk];
    }
}
- (void)handleInterruption:(NSNotification *)notification
{
    NSUInteger interruptionType = [[[notification userInfo]
                                    objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
        if (isBackGround) {
            return;
        }
        [_es stopTalk];
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
        if (self.isInterrupt == NO) {
            return;
        }else{
            //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
            [self InterruptionEndedAVAudioSessionSetActiveYES];
        }
    }
}
//AVAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
- (void) sessionWasInterrupted:(NSNotification*)notification
{
    if (_playing == YES) {
        self.isInterrupt = YES;
        //AVCaptureSessionInterruptionReason
        if (@available(iOS 9.0, *)) {
            NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue]; //电话中断是1
            NSLog(@"Capture session was interrupted with reason %ld", (long)reason);
            //音频硬件暂时不可用而造成的中断,例如,电话或警报。
            if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient ||
                reason == AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient");
                //VAudioPlayer ç±»å’Œ AVAudioRecorder ç±»ï¼Œå½“发生中断时,系统会自动暂停播放或录制
                //Audio Queue Services, I/O audio unit
                [_es onStopCapture];
                [_es stopTalk];
                /*
                 NSError *error = nil;
                 [[AVAudioSession sharedInstance] setActive:NO error:&error];
                 if (error) {
                 NSLog(@"sessionWasInterruptedSetActiveNO error:%@", error);
                 }
                 */
            }else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground){
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground");
                //如果是电话中断,不会走进入后台的通知,进入后台再切换到前台这里是不用处理的
                if (isBackGround) {
                    return;
                }
                [_es onStopCapture];
                [_es stopTalk];
            }
            //多个应用程序资源争用质量下降。只有当应用程序占据全屏时,会话才能运行。
            else if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps) {
                NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps");
                // Fade-in a label to inform the user that the camera is unavailable.
            }else if (@available(iOS 11.1, *)) {
                if (reason == AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure){
                    NSLog(@"AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableDueToSystemPressure");
                }
            } else {
                // Fallback on earlier versions
            }
        } else {
            if (isBackGround) {
                return;
            }
            [_es onStopCapture];
            [_es stopTalk];
        }
    }
}
//这个通知可能会获取不到,
- (void) sessionInterruptionEnded:(NSNotification*)notification
{
    //    NSInteger reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
    NSLog(@"Capture session interruption ended");
    if (self.isInterrupt == NO) {
        return;
    }else{
        //直接在进入前台那个通知里面实现,实际上进入前台的方法会在这个方法前面调用,效果更好
        [self InterruptionEndedAVAudioSessionSetActiveYES];
    }
}
-(void)InterruptionEndedAVAudioSessionSetActiveYES{
    if (isBackGround) {
        return;
    }
    if (self.isInterrupt == YES) {
        [_es onStartCapture];
        [_es startTalk];
        self.isInterrupt = NO;
    }
}
- (void)speaker:(UIButton *)sender {
    [_es stopTalk];
    NSString *result = nil;
    //    //听筒状态 æ’耳塞后拔掉后恢复到默认设置
    //    if (sender == nil) {
    //        result = [_sessionHelper speaker:NO];
    //    }else{
    //        if(!_isSpeaking){
    //            result = [_sessionHelper speaker:YES];
    //            _isSpeaking = YES;
    //        }else{
    //            result = [_sessionHelper speaker:NO];
    //            _isSpeaking = NO;
    //        }
    //    }
    //    if (result) {
    //        [sender setTitle:result forState:UIControlStateNormal];
    //        [_es startTalk];
    //    }
    if(!_isSpeaking){
        result = [_sessionHelper speaker:YES];
        _isSpeaking = YES;
    }else{
        result = [_sessionHelper speaker:NO];
        _isSpeaking = NO;
    }
    if (result) {
        //          [sender setTitle:result forState:UIControlStateNormal];
        [_es startTalk];
    }
}
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
    NSDictionary *interuptionDict = notification.userInfo;
    NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (routeChangeReason) {
        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
            //免提状态下耳机插入没有采集,同意切换到默认状态
            NSLog(@"耳机插入");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
            //NSLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
            NSLog(@"耳机拔出");
            [self speaker:nil];
            break;
        case AVAudioSessionRouteChangeReasonCategoryChange:
            // called at start - also when other audio wants to play
            //NSLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
            break;
    }
}
/*
 éœ€è¦æ³¨æ„çš„æ˜¯ï¼Œæœ‰ä¸€ä¸ªä¸­æ–­å¼€å§‹æ¶ˆæ¯ä¸ä¸€å®šä¼šæœ‰ä¸€ä¸ªä¸­æ–­ç»“束消息,这就意味着中断结束的回调里的处理逻辑可能会没有被执行到。
 æ‰€ä»¥éœ€è¦å…³æ³¨å½“切到前台运行状态时,是不是需要重新激活你的 Audio Session。
 */
- (void)willEnterForeground:(NSNotification*)notification{
    NSLog(@"willEnterForeground");
    //初次启动会走这个通知(根页面),这时候是没有进入后台的
    if (isBackGround) {
        return;
    }
    [self InterruptionEndedAVAudioSessionSetActiveYES];
    // è¿™é‡Œæ˜¯è€ƒè™‘到用户没有授权,之后通过AlertAction跳转到设置页面授权后再回到APP时做的重新检测
    //跳转到设置页面,授权后返回页面,继续初始化采集器
    if(!isAccessAudio){
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
        if (isAccessAudio) {
            [_es initAudioCaptureSession];
        }
    }
//    if (isAccessAudio && isAccessVideo) {
//        return;
//    }
//    if (isAccessVideo && !isAccessAudio) {
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
//    }else if (!isAccessVideo && isAccessAudio){
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//
//    }else if (!isAccessVideo && !isAccessAudio){
//        [self requestAccessForAVMediaType:AVMediaTypeAudio];
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//        if (isAccessAudio) {
//            [_es initAudioCaptureSession];
//        }
////        if (isAccessVideo) {
////            [_es initVideoCaptureSession];
////        }
//    }
}
- (void)willEnterBackground:(NSNotification *)notification {
    isBackGround = YES;
}
//授权Alert
-(void)requestAccessForAVMedia{
    if (!isAccessAudio) {
        [self requestAccessForAVMediaType:AVMediaTypeAudio];
    }
//    if (!isAccessVideo) {
////        [self requestAccessForAVMediaType:AVMediaTypeVideo];
//    }
    if (!iSAudioNotDetermined && iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"相机没有授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }else if(!iSAudioNotDetermined && !iSVideoNotDetermined){
        [self creatAlertViewWith:@"授权请求" message:@"麦克风与相机授权,请在设置中开启权限,否则将影响通讯功能。" cancel:@"确定"];
    }
}
-(void)requestAccessForAVMediaType:(AVMediaType)type{
    if (type == AVMediaTypeVideo) {
        isAccessVideo = YES;
        iSVideoNotDetermined = YES;
    }else{
        isAccessAudio = YES;
        iSAudioNotDetermined = YES;
    }
    switch ([AVCaptureDevice authorizationStatusForMediaType:type])
    {
        case AVAuthorizationStatusAuthorized:
        {
            break;
        }
        case AVAuthorizationStatusNotDetermined:
        {
            dispatch_suspend(dispatch_get_main_queue());
            [AVCaptureDevice requestAccessForMediaType:type completionHandler:^(BOOL granted) {
                if (!granted) {
                    if (type == AVMediaTypeVideo) {
                        self->isAccessVideo = NO;
                    }else{
                        self->isAccessAudio = NO;
                    }
                }
                dispatch_resume(dispatch_get_main_queue());
            }];
            break;
        }
        default:
        {
            if (type == AVMediaTypeVideo) {
                isAccessVideo = NO;
                iSVideoNotDetermined = NO;
            }else{
                isAccessAudio = NO;
                iSAudioNotDetermined = NO;
            }
            break;
        }
    }
}
-(void)creatAlertViewWith:(NSString *)title message:(NSString *) msg cancel:(NSString *)cancelMsg{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:cancelMsg style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}
#pragma ä¿å­˜å›¾ç‰‡åˆ°ç›¸å†Œ
- (void)saveImageToPhotosAlbum:(UIImage *)savedImage
{
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined)
    {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if(status == PHAuthorizationStatusAuthorized)
            {
                UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    }
    else
    {
        if (status == PHAuthorizationStatusAuthorized)
        {
            UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}
// æŒ‡å®šå›žè°ƒæ–¹æ³•
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSString *message = nil;
    if (!error) {
        message = saveToTheAlbumsStr;
    }
    else
    {
        message = operationFailedStr;
    }
    [self showUIAlertView:message];
}
@end
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESOnUtlis.h
New file
@@ -0,0 +1,65 @@
//
//  ESOnUtlis.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2021/3/11.
//  Copyright Â© 2021 eTouchSky. All rights reserved.
//
#define TP_SYSTEM_VERSION ([[[UIDevice currentDevice] systemVersion] floatValue])
//#define IOS6 ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0)
#define IOS7  (TP_SYSTEM_VERSION >= 7.0)
#define IOS8  (TP_SYSTEM_VERSION >= 8.0)
#define IOS9  (TP_SYSTEM_VERSION >= 9.0)
#define IOS10 (TP_SYSTEM_VERSION >= 10.0)
//是否是 iPhoneX_XS_11Pro设备
#define IPhoneX ([UIScreen mainScreen].bounds.size.width == 375.0f && [UIScreen mainScreen].bounds.size.height == 812.0f)
//是否是 iPhoneXSMax_XR_11_11ProMax
#define IPhoneXR ([UIScreen mainScreen].bounds.size.width == 414.0f && [UIScreen mainScreen].bounds.size.height == 896.0f)
// æ˜¯å…¨é¢å±æ‰‹æœº
#define IsFullScreen (IPhoneX || IPhoneXR)
// Screen
#define APP_SCREEN_BOUNDS   [[UIScreen mainScreen] bounds]
#define APP_SCREEN_HEIGHT   (APP_SCREEN_BOUNDS.size.height)
#define APP_SCREEN_WIDTH    (APP_SCREEN_BOUNDS.size.width)
#define APP_STATUS_FRAME    [UIApplication sharedApplication].statusBarFrame
#define APP_TOP_BAR_HEIGHT    (IsFullScreen ? 88 : (IOS7 ? 64 : 44))
#define APP_STATUS_BAR_HEIGHT (IsFullScreen ? 44: (IOS7 ? 20 : 0))
#define APP_STATUS_BAR_HEIGHT_TURE (IsFullScreen ? 44: (IOS7 ? 0 : 0))
#define APP_TOOL_BAR_HEIGHT   49
#define APP_TAB_BAR_HEIGHT    (IsFullScreen ? (49 + 34): 49)
#define APP_CONTENT_WIDTH     (APP_SCREEN_BOUNDS.size.width)
#define APP_BUTTOM_HEIGHT     (IsFullScreen ? 34: 0)
#define APP_CONTENT_HEIGHT    (APP_SCREEN_HEIGHT - APP_TOP_BAR_HEIGHT - APP_TAB_BAR_HEIGHT)
#define APP_VISIBLE_HEIGHT    (APP_SCREEN_HEIGHT - APP_TOP_BAR_HEIGHT -APP_BUTTOM_HEIGHT)
#define APP_B_HEIGHT          (APP_SCREEN_HEIGHT - APP_BUTTOM_HEIGHT)
#define APP_UIFont            @"PingFangSC-Regular"
#define APP_UIFont_BOLD       @"PingFangSC-Semibold"
#define DesignWidth 375
#define DesignHeight 667
#define GetRealWidth(W)  W / (DesignWidth * 1.0f / APP_SCREEN_WIDTH)
#define GetRealHeight(H)  H / (DesignHeight * 1.0f / APP_SCREEN_HEIGHT)
#define TextColor    HEXCOLORA(0x333333, 1.0)
#define TextSelectColor    HEXCOLORA(0xFB744A, 1.0)
/** WEAKSELF_AT */
#define WEAKSELF_AT __weak __typeof(&*self)weakSelf_AT = self;
#define HEXCOLORA(rgbValue, a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 green:((float)((rgbValue & 0xFF00) >> 8)) / 255.0 blue:((float)(rgbValue & 0xFF)) / 255.0 alpha:a]
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESOnUtlis : NSObject
@end
NS_ASSUME_NONNULL_END
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESVideo.h
New file
@@ -0,0 +1,26 @@
//
//  ESVideo.h
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/28.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ESVideoPhoneSDk/ESVideoPhone.h>
#import <ESVideoPhoneSDk/ESError.h>
NS_ASSUME_NONNULL_BEGIN
@interface ESVideo : NSObject
@property (nonatomic,strong) ESVideoPhone *es;
@property (nonatomic, copy) ImageCallback snapImageCallback;
+ (instancetype)shareInstance;
+ (void)haltSharedInstance;
-(void)initSDK;
@end
NS_ASSUME_NONNULL_END
On+/xamarin/ESVideoOnXamarin/ESVideoOnXamarin/ESVideo.m
New file
@@ -0,0 +1,78 @@
//
//  ESVideo.m
//  ESVideoPhoneSDKDemo
//
//  Created by é™ˆå˜‰ä¹ on 2020/6/28.
//  Copyright Â© 2020 eTouchSky. All rights reserved.
//
#import "ESVideo.h"
#import "ESOnUtlis.h"
#define WEAKSELF_AT __weak __typeof(&*self)weakSelf_AT = self;
@interface ESVideo()
@end
static ESVideo *_sharedInstance = nil;
static dispatch_once_t _onceToken;
@implementation ESVideo{
    ImageCallback mSnapImageCallback;
}
+ (instancetype)shareInstance {
    dispatch_once(&_onceToken, ^{
        _sharedInstance = [[ESVideo alloc] init];
        if(_sharedInstance) {
        }
        NSLog(@"ESVideo ShareInstance Did Create %p",_sharedInstance);
    });
    //NSLog(@"Access ESVideo %p",sharedInstance);
    return _sharedInstance;
}
+ (void)haltSharedInstance {
    if (_sharedInstance) {
//        if(_sharedInstance.es){
//            [_sharedInstance.es freeSubClass];
//        }
        _sharedInstance = nil;
        _onceToken = 0;
    }
}
- (instancetype)init {
    self = [super init];
    WEAKSELF_AT
    mSnapImageCallback = ^(UIImage *image){
        if(weakSelf_AT.snapImageCallback){
            weakSelf_AT.snapImageCallback(image);
        }
    };
    //门口机会有视频的长宽高,是固定的(暂时还不确定)
    _es = [[ESVideoPhone alloc]initESVideoPhoneWithFrame: CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, GetRealWidth(210)) delegate:nil imagecallBack:mSnapImageCallback];
    return self;
}
- (void)dealloc {
    NSLog(@"ESVideo SharedInstance Did Halted ");
}
-(void)initSDK{
     NSLog(@"ESVideo initSDK");
}
@end
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/ESVideoPhoneSDk
Binary files differ
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Headers/ESError.h
New file
@@ -0,0 +1,76 @@
//
//  ESError.h
//  ESVideoPhoneSDk
//
//  Created by eTouchSky on 2019/12/14.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//
//  ESVideoPhone的ESVideoPhoneDelegate方法“- (void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger) errortype” ä¸­errortype的类型
#import <Foundation/Foundation.h>
//视频采集,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
//App需要处理 LMPVideoCaptureErrorGetDevice与LMPVideoCaptureErrorNotAuthorized ç±»åž‹çš„错误
typedef NS_ENUM (NSUInteger, LMPVideoCaptureError){
    LMPVideoCaptureErrorGetDevice                      = 0,//获取摄像头失败
    LMPVideoCaptureErrorGetInput                       = 1,//获取视频输入错误,从error中获取具体信息
    LMPVideoCaptureErrorAddInput                       = 2,//AddInputVideo错误
    LMPVideoCaptureErrorAddOutput                      = 3,//AddOutputVideo错误
    LMPVideoCaptureErrorNotAuthorized                  = 4,//没有授权,授权处理由App实现
    LMPVideoCaptureErrorSetSessionPreset               = 5,//SetSessionPreset失败
};
//openGl视频渲染,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
typedef NS_ENUM (NSUInteger, LMPOpenglError){
    LMPOpenglErrorSetCurrentContext                      = 0,//EAGLContext上下文创建失败
    LMPOpenglErrorRenderbuffer                           = 1,//OpenGL attach渲染缓冲区失败
    LMPOpenglErrorGenTextures                            = 2,//OpenGL çº¹ç†åˆ›å»ºå¤±è´¥
    LMPOpenglErrorShaderConnection                       = 3,//OpenGL ç€è‰²å™¨è¿žæŽ¥å¤±è´¥
    LMPOpenglErrorLoadShader                             = 4,//OpenGL åŠ è½½ç€è‰²å™¨å¤±è´¥
    LMPOpenglErrorShaderCompilation                      = 5,//OpenGL ç€è‰²å™¨ç¼–译失败
};
/**视频解码,会导致App看不到门口机的视频
debug模式下,断点可能导致出现LMPH264HwDecoderErrorKVTInvalidSessionErr的错误
前台后切换,来电等原因的中断可能会出现LMPH264HwDecoderErrorKVTInvalidSessionErr的错误(已经修复,注意测试过程中是否还会出现)
*/
typedef NS_ENUM (NSUInteger, LMPH264HwDecoderError){
    LMPH264HwDecoderErrorInitH264HardwareDecoder                     = 0,//InitH264HardwareDecoderErr
    LMPH264HwDecoderErrorKVTVideoDecoderMalfunction                  = 1,//kVTVideoDecoderMalfunctionErr
    LMPH264HwDecoderErrorKVTInvalidSessionErr                        = 2,//kVTInvalidSessionErr
    LMPH264HwDecoderErrorKVTVideoDecoderBadDataErr                   = 3,//kVTVideoDecoderBadDataErr
    LMPH264HwDecoderErrorDecodeOtherErr                              = 4,//硬解码其他错误
    LMPH264HwDecoderErrorCVPixelBufferRefPlantErr                    = 5,//CVPixelBufferRef中数据为空
    LMPH264HwDecoderErrorCVPixelBufferRefYUVErr                      = 6,//CVPixelBufferRef中取出YUV失败
};
//视频编码,会导致门口机看不到App的视频(监控状态时,门口机是看不到App的视频的)
typedef NS_ENUM (NSUInteger, LMPH264EncoderError){
    LMPH264EncoderErrorVTCompressionSessionCreate                    = 0,//VTCompressionSessionCreateErr
    LMPH264EncoderErrorVideoCompressonOutputCallback                 = 1,//编码回调失败
};
//音频采集,会导致不能采集音频,对方听不到App的声音(监控状态时,门口机是听不到App的声音的)
typedef NS_ENUM (NSUInteger, LMPAudioCaptureError){
    LMPAudioCaptureErrorGetNew                         = 0,//AudioComponentInstanceNew失败
    LMPAudioCaptureErrorStreamFormat                   = 1,//kAudioUnitProperty_StreamFormat失败
    LMPAudioCaptureErrorEnableRecord                   = 2,//打开录音失败
    LMPAudioCaptureErrorEnablePlay                     = 3,//关闭播放失败
    LMPAudioCaptureErrorSetInputCallback               = 4,//AudioUnitSetCallback失败
    LMPAudioCaptureErrorShouldAllocateBuffer           = 5,//使用自己的缓存失败
    LMPAudioCaptureErrorBypassVoiceProcessing          = 6,//打开回声消除功能失败
    LMPAudioCaptureErrorAudioUnitInitialize          = 7,//AudioUnitInitialize失败
};
//音频播放,会导致不能播放音频,听不到对方的声音
typedef NS_ENUM (NSUInteger, LMPAudioPlayError){
    LMPAudioPlayErrorGetNew                         = 0,//AudioPlay_AudioComponentInstanceNew失败
    LMPAudioPlayErrorStreamFormat                   = 1,//AudioPlay_kAudioUnitProperty_StreamFormat失败
    LMPAudioPlayErrorEnableRecord                   = 2,//AudioPlay_关闭录音失败
    LMPAudioPlayErrorEnablePlay                     = 3,//AudioPlay_打开播放失败
    LMPAudioPlayErrorSetRenderCallback              = 4,//AudioPlay_AudioUnitSetRenderCallback失败
    LMPAudioPlayErrorShouldAllocateBuffer           = 5,//使用自己的缓存失败
    LMPAudioPlayErrorBypassVoiceProcessing          = 6,//AudioPlay_打开回声消除功能失败
    LMPAudioPlayErrorAudioUnitInitialize            = 7,//AudioPlay_AudioUnitInitialize失败
};
@interface ESError : NSObject
@end
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Headers/ESVideoPhone.h
New file
@@ -0,0 +1,83 @@
//  ESVideoPhone.h
//  VideoPhone
//
//  Created by maygion on 2019/3/26.
//  Copyright Â© 2019 eTouchSky. All rights reserved.
//  SDK版本号:1.0.0  æµ‹è¯•版1
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void (^ImageCallback)(UIImage *image);
@protocol ESVideoPhoneDelegate <NSObject>
- (void)getAErrorForESVideoPhone:(NSString *)errorStr type:(NSUInteger) errortype;
- (void)getPhoneEvent_UI:(NSString*)phoneEvent;
@end
@interface ESVideoPhone : NSObject
//错误信息
@property(nonatomic,weak) id<ESVideoPhoneDelegate> delegate;
////获取视频截图的block
//@property(nonatomic,copy) ImageCallback snapImageCallback;
//视频显示的View âš ï¸è¯·åšéžç©ºåˆ¤æ–­
@property(nonatomic,strong,readonly) UIView* showView;
//中断设置
@property (nonatomic,assign,readwrite) BOOL isInterrupt;
/**
* ESVideoPhone åˆå§‹åŒ–方法
*
* @param frame ç”Ÿæˆæ˜¾ç¤ºè§†é¢‘çš„CGRect
* @param delegate ESVideoPhoneDelegate代理
* @paramimagecallBack èŽ·å–è§†é¢‘æˆªå›¾çš„block
*
*  @return ESVideoPhone å®žä¾‹,返回nil表示初始化失败。
*/
- (instancetype)initESVideoPhoneWithFrame:(CGRect)frame delegate: (id<ESVideoPhoneDelegate>)del imagecallBack:(ImageCallback)callBack;
//授权后打开音频采集,视频没有权限也可以只音频通话
-(BOOL)initAudioCaptureSession;
//⚠️授权后打开视频采集,音频没有权限建议不要发起通话
-(BOOL)initVideoCaptureSession;
/**
* ç›‘控
*
* @param deviceId é—¨å£æœºè®¾å¤‡ID,这个参数会从后台拉取(一般是个数组其中包含该用户权限拥有的所有门口机)
*/
- (void)onMonitor:(NSString *)deviceId;
/**
* æŽ¥å¬
* è¿™ä¸ªæ–¹æ³•会在AppDelegate的通知接口中调用,采用小米推送
* @param deviceId é—¨å£æœºè®¾å¤‡ID,这个参数会从通知中获取由设备ID与单次通话tag拼成(可能会有变动)
*/
- (void)onReverseCall:(NSString *)deviceId;
//接听
- (void)onAccept;
//挂断
- (void)onHangup;
//打开音频采集与音频播放
-(void)startTalk;
//关闭音频采集与音频播放
- (void)stopTalk;
//获取视频截图
- (void)onSnap;
//打开视频采集
- (void)onStartCapture;
//关闭视频采集
- (void)onStopCapture;
/**
* å¼€é—¨ï¼Œç»“果在ESVideoPhoneDelegate æ–¹æ³•中返回
*
* @param roomId ä½æˆ·çš„roomid)
*/
-(void)openTheDoorWithRoomid: (int)roomId;
//释放资源(delegate释放等)
-(void)freeSubClass;
@end
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/Info.plist
Binary files differ
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeDirectory
Binary files differ
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements
Binary files differ
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeRequirements-1
Binary files differ
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeResources
New file
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>files</key>
    <dict>
        <key>Headers/ESError.h</key>
        <data>
        6x23lohs1gRaX2F7raZcezxZt4A=
        </data>
        <key>Headers/ESVideoPhone.h</key>
        <data>
        PXntC+GjEn5A+q23YZHDZYIGj3k=
        </data>
        <key>Info.plist</key>
        <data>
        pac6jrrTIT3Ur/3XHh3CoCOUlnE=
        </data>
    </dict>
    <key>files2</key>
    <dict>
        <key>Headers/ESError.h</key>
        <dict>
            <key>hash</key>
            <data>
            6x23lohs1gRaX2F7raZcezxZt4A=
            </data>
            <key>hash2</key>
            <data>
            y8ReVa9YaZ6fyPjy8EeTl3jbaUadZUYVC0IdLJ/HrgM=
            </data>
        </dict>
        <key>Headers/ESVideoPhone.h</key>
        <dict>
            <key>hash</key>
            <data>
            PXntC+GjEn5A+q23YZHDZYIGj3k=
            </data>
            <key>hash2</key>
            <data>
            MboPVH5djlpgR//Z7UcRBMEeaakW6FWQbZxlkx4KzaM=
            </data>
        </dict>
    </dict>
    <key>rules</key>
    <dict>
        <key>^.*</key>
        <true/>
        <key>^.*\.lproj/</key>
        <dict>
            <key>optional</key>
            <true/>
            <key>weight</key>
            <real>1000</real>
        </dict>
        <key>^.*\.lproj/locversion.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>1100</real>
        </dict>
        <key>^Base\.lproj/</key>
        <dict>
            <key>weight</key>
            <real>1010</real>
        </dict>
        <key>^version.plist$</key>
        <true/>
    </dict>
    <key>rules2</key>
    <dict>
        <key>.*\.dSYM($|/)</key>
        <dict>
            <key>weight</key>
            <real>11</real>
        </dict>
        <key>^(.*/)?\.DS_Store$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>2000</real>
        </dict>
        <key>^.*</key>
        <true/>
        <key>^.*\.lproj/</key>
        <dict>
            <key>optional</key>
            <true/>
            <key>weight</key>
            <real>1000</real>
        </dict>
        <key>^.*\.lproj/locversion.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>1100</real>
        </dict>
        <key>^Base\.lproj/</key>
        <dict>
            <key>weight</key>
            <real>1010</real>
        </dict>
        <key>^Info\.plist$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^PkgInfo$</key>
        <dict>
            <key>omit</key>
            <true/>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^embedded\.provisionprofile$</key>
        <dict>
            <key>weight</key>
            <real>20</real>
        </dict>
        <key>^version\.plist$</key>
        <dict>
            <key>weight</key>
            <real>20</real>
        </dict>
    </dict>
</dict>
</plist>
On+/xamarin/ESVideoOnXamarin/Extern/ESVideoPhoneSDk.framework/_CodeSignature/CodeSignature
Binary files differ
On+/xamarin/ESVideoOnXamarin/README.md
New file
@@ -0,0 +1,6 @@
## ä¸°æž—可视对讲
### *****V1.0.1*****
### 1.ON+版本丰林可视对讲。
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK.sln
New file
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.808.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.IOS.ESVideoOnSDK", "Shared.IOS.ESVideoOnSDK\Shared.IOS.ESVideoOnSDK.csproj", "{D1832CED-B861-4FE3-A3BD-3D582C108D3A}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
        Release|Any CPU = Release|Any CPU
    EndGlobalSection
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
        {D1832CED-B861-4FE3-A3BD-3D582C108D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {D1832CED-B861-4FE3-A3BD-3D582C108D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {D1832CED-B861-4FE3-A3BD-3D582C108D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {D1832CED-B861-4FE3-A3BD-3D582C108D3A}.Release|Any CPU.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
    EndGlobalSection
    GlobalSection(ExtensibilityGlobals) = postSolution
        SolutionGuid = {27227BDD-DE4C-42C8-9FF5-9DE1CE59EF1D}
    EndGlobalSection
EndGlobal
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/ApiDefinition.cs
New file
@@ -0,0 +1,79 @@
using System;
using ObjCRuntime;
using Foundation;
using UIKit;
namespace Shared.IOS.ESVideoOnSDK
{
    // @interface ESOnIntercomViewController : UIViewController
    [BaseType(typeof(UIViewController))]
    interface ESOnIntercomViewController
    {
        // @property (nonatomic, strong) NSString * _Nonnull mESVideoID;
        [Export("mESVideoID", ArgumentSemantic.Strong)]
        string MESVideoID { get; set; }
        // @property (assign, nonatomic) int mESRoomID;
        [Export("mESRoomID")]
        int MESRoomID { get; set; }
        // @property (nonatomic, strong) NSString * _Nonnull roomName;
        [Export("roomName", ArgumentSemantic.Strong)]
        string RoomName { get; set; }
        // @property (nonatomic, strong) NSString * _Nonnull deviceName;
        [Export("deviceName", ArgumentSemantic.Strong)]
        string DeviceName { get; set; }
    }
    // @interface ESOnMonitorViewController : UIViewController
    [BaseType(typeof(UIViewController))]
    interface ESOnMonitorViewController
    {
        // @property (nonatomic, strong) NSString * _Nonnull mESVideoID;
        [Export("mESVideoID", ArgumentSemantic.Strong)]
        string MESVideoID { get; set; }
        // @property (assign, nonatomic) int mESRoomID;
        [Export("mESRoomID")]
        int MESRoomID { get; set; }
        // @property (nonatomic, strong) NSString * _Nonnull roomName;
        [Export("roomName", ArgumentSemantic.Strong)]
        string RoomName { get; set; }
        // @property (nonatomic, strong) NSString * _Nonnull deviceName;
        [Export("deviceName", ArgumentSemantic.Strong)]
        string DeviceName { get; set; }
    }
    // @interface ESVideo : NSObject
    [BaseType(typeof(NSObject))]
    interface ESVideo
    {
        //// @property (nonatomic, strong) int * _Nonnull es;
        //[Export("es", ArgumentSemantic.Strong)]
        //unsafe int* Es { get; set; }
        //// @property (copy, nonatomic) int snapImageCallback;
        //[Export("snapImageCallback", ArgumentSemantic.Copy)]
        //int SnapImageCallback { get; set; }
        // +(instancetype _Nonnull)shareInstance;
        [Static]
        [Export("shareInstance")]
        ESVideo ShareInstance();
        // +(void)haltSharedInstance;
        [Static]
        [Export("haltSharedInstance")]
        void HaltSharedInstance();
        // -(void)initSDK;
        [Export("initSDK")]
        void InitSDK();
    }
}
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Library/ESVideoPhoneSDk.a
Binary files differ
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Library/libESVideoOnXamarin.a
Binary files differ
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Properties/AssemblyInfo.cs
New file
@@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using Foundation;
// This attribute allows you to mark your assemblies as â€œsafe to link”.
// When the attribute is present, the linker—if enabled—will process the assembly
// even if you’re using the â€œLink SDK assemblies only” option, which is the default for device builds.
[assembly: LinkerSafe]
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("Shared.IOS.ESVideoOnSDK")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shared.IOS.ESVideoOnSDK")]
[assembly: AssemblyCopyright("Copyright Â©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.1")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_answer.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_back.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_hangup.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_takephoto_select.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_takephoto_unselect.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_unlock_select.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Resources/ic_esvideo_on_unlock_unselect.png
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK.csproj
New file
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectTypeGuids>{8FFB629D-F513-41CE-95D2-7ECE97B6EEEC};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <ProjectGuid>{D1832CED-B861-4FE3-A3BD-3D582C108D3A}</ProjectGuid>
    <TemplateGuid>{b6f3ff35-79b2-4f25-a2fc-60a7cf61013b}</TemplateGuid>
    <OutputType>Library</OutputType>
    <RootNamespace>Shared.IOS.ESVideoOnSDK</RootNamespace>
    <IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
    <AssemblyName>Shared.IOS.ESVideoOnSDK</AssemblyName>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug</OutputPath>
    <DefineConstants>DEBUG;</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>full</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release</OutputPath>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="Xamarin.iOS" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Resources\" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ObjcBindingApiDefinition Include="ApiDefinition.cs" />
  </ItemGroup>
  <ItemGroup>
    <ObjcBindingCoreSource Include="Structs.cs" />
  </ItemGroup>
  <ItemGroup>
    <NativeReference Include="Library\libESVideoOnXamarin.a">
      <Kind>Static</Kind>
      <ForceLoad>True</ForceLoad>
      <LinkerFlags>-dead_strip -Wl -lz -lbz2 -liconv.2.4.0 -ObjC</LinkerFlags>
    </NativeReference>
    <NativeReference Include="Library\ESVideoPhoneSDk.a">
      <Kind>Static</Kind>
      <SmartLink>False</SmartLink>
    </NativeReference>
  </ItemGroup>
  <ItemGroup>
    <BundleResource Include="Resources\ic_esvideo_on_answer.png" />
    <BundleResource Include="Resources\ic_esvideo_on_back.png" />
    <BundleResource Include="Resources\ic_esvideo_on_hangup.png" />
    <BundleResource Include="Resources\ic_esvideo_on_takephoto_select.png" />
    <BundleResource Include="Resources\ic_esvideo_on_takephoto_unselect.png" />
    <BundleResource Include="Resources\ic_esvideo_on_unlock_select.png" />
    <BundleResource Include="Resources\ic_esvideo_on_unlock_unselect.png" />
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.ObjCBinding.CSharp.targets" />
</Project>
On+/xamarin/Shared.IOS.ESVideoOnSDK/Shared.IOS.ESVideoOnSDK/Structs.cs
New file
@@ -0,0 +1,6 @@
using System;
namespace Shared.IOS.ESVideoOnSDK
{
}