diff --git a/Info-100.png b/Info-100.png new file mode 100644 index 0000000..deac4aa Binary files /dev/null and b/Info-100.png differ diff --git a/R5ProTestbed.xcodeproj/project.pbxproj b/R5ProTestbed.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7881b7b --- /dev/null +++ b/R5ProTestbed.xcodeproj/project.pbxproj @@ -0,0 +1,479 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 485345071C23528400D409F3 /* PublishStreamImageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485345061C23528400D409F3 /* PublishStreamImageTest.swift */; }; + 485345091C23552F00D409F3 /* CameraSwapTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 485345081C23552F00D409F3 /* CameraSwapTest.swift */; }; + 4874D59D1C2216DB00A98102 /* SubscribeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4874D59C1C2216DB00A98102 /* SubscribeTest.swift */; }; + 4874D59F1C221B9500A98102 /* AdaptiveBitrateControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4874D59E1C221B9500A98102 /* AdaptiveBitrateControllerTest.swift */; }; + 4874D5A31C221CD000A98102 /* ALToastView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4874D5A21C221CD000A98102 /* ALToastView.m */; }; + 4874ECD01C483FDA002BDA6D /* SubscribeStreamImageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4874ECCF1C483FDA002BDA6D /* SubscribeStreamImageTest.swift */; }; + 48BB1D811CED140D00D915E5 /* SubscribeSetSizeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48BB1D801CED140D00D915E5 /* SubscribeSetSizeTest.swift */; }; + 48BF80AE1C245AA7000E996E /* PublishOrientationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48BF80AD1C245AA7000E996E /* PublishOrientationTest.swift */; }; + 48BF80B11C245D29000E996E /* Info-100.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BF80B01C245D29000E996E /* Info-100.png */; }; + 48BF80B31C246F6D000E996E /* SubscribeAspectRatioTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48BF80B21C246F6D000E996E /* SubscribeAspectRatioTest.swift */; }; + 48DFEDAC1C21BBC60040B624 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDAB1C21BBC60040B624 /* AppDelegate.swift */; }; + 48DFEDAE1C21BBC60040B624 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDAD1C21BBC60040B624 /* MasterViewController.swift */; }; + 48DFEDB01C21BBC60040B624 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDAF1C21BBC60040B624 /* DetailViewController.swift */; }; + 48DFEDB31C21BBC60040B624 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48DFEDB11C21BBC60040B624 /* Main.storyboard */; }; + 48DFEDB51C21BBC60040B624 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 48DFEDB41C21BBC60040B624 /* Assets.xcassets */; }; + 48DFEDB81C21BBC60040B624 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48DFEDB61C21BBC60040B624 /* LaunchScreen.storyboard */; }; + 48DFEDCD1C21BF0A0040B624 /* PublishTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDCC1C21BF0A0040B624 /* PublishTest.swift */; }; + 48DFEDCF1C21BF2D0040B624 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDCE1C21BF2D0040B624 /* CoreFoundation.framework */; }; + 48DFEDD11C21BF320040B624 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDD01C21BF320040B624 /* QuartzCore.framework */; }; + 48DFEDD31C21BF370040B624 /* libiconv.2.4.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDD21C21BF370040B624 /* libiconv.2.4.0.tbd */; }; + 48DFEDD51C21C18F0040B624 /* Home.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDD41C21C18F0040B624 /* Home.swift */; }; + 48DFEDD71C21C2540040B624 /* BaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDD61C21C2540040B624 /* BaseTest.swift */; }; + 48DFEDD91C21C5940040B624 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDD81C21C5940040B624 /* OpenAL.framework */; }; + 48DFEDDB1C21C5AB0040B624 /* libstdc++.6.0.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDDA1C21C5AB0040B624 /* libstdc++.6.0.9.tbd */; }; + 48DFEDDD1C21E91E0040B624 /* tests.plist in Resources */ = {isa = PBXBuildFile; fileRef = 48DFEDDC1C21E91E0040B624 /* tests.plist */; }; + 48DFEDDF1C21EA780040B624 /* Testbed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DFEDDE1C21EA780040B624 /* Testbed.swift */; }; + 926794F51D061900004B0BCD /* PublishRemoteCallTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794F41D061900004B0BCD /* PublishRemoteCallTest.swift */; }; + 926794F71D06191E004B0BCD /* PublishStreamManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794F61D06191E004B0BCD /* PublishStreamManagerTest.swift */; }; + 926794F91D061942004B0BCD /* RecordedTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794F81D061942004B0BCD /* RecordedTest.swift */; }; + 926794FB1D061968004B0BCD /* SubscribeCluster.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794FA1D061968004B0BCD /* SubscribeCluster.swift */; }; + 926794FD1D061983004B0BCD /* SubscribeRemoteCallTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794FC1D061983004B0BCD /* SubscribeRemoteCallTest.swift */; }; + 926794FF1D06199E004B0BCD /* TwoWayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 926794FE1D06199E004B0BCD /* TwoWayTest.swift */; }; + 928B1CBD1CBC0DC5002C66BA /* SubscribeStreamManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928B1CBC1CBC0DC5002C66BA /* SubscribeStreamManagerTest.swift */; }; + 92CC78091CB6F8DD00CD4352 /* SubscribeTwoStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92CC78081CB6F8DD00CD4352 /* SubscribeTwoStreams.swift */; }; + 92E13BF41CDD165800DA5783 /* SubscribeNoViewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92E13BF31CDD165800DA5783 /* SubscribeNoViewTest.swift */; }; + 92EA19DA1CE13717006B3A97 /* PublishCustomSourceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92EA19D91CE13717006B3A97 /* PublishCustomSourceTest.swift */; }; + 92EA19DD1CE137DD006B3A97 /* CustomVideoSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92EA19DC1CE137DD006B3A97 /* CustomVideoSource.swift */; }; + 92EAA9DB1D0734EA00B2CE46 /* SubscribeAutoReconnectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92EAA9DA1D0734EA00B2CE46 /* SubscribeAutoReconnectTest.swift */; }; + 92F9A0681D020BA100F49644 /* R5Streaming.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DFEDC51C21BBDB0040B624 /* R5Streaming.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 485345061C23528400D409F3 /* PublishStreamImageTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishStreamImageTest.swift; path = Tests/PublishStreamImage/PublishStreamImageTest.swift; sourceTree = ""; }; + 485345081C23552F00D409F3 /* CameraSwapTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CameraSwapTest.swift; path = Tests/CameraSwap/CameraSwapTest.swift; sourceTree = ""; }; + 4874D59C1C2216DB00A98102 /* SubscribeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeTest.swift; path = Tests/Subscribe/SubscribeTest.swift; sourceTree = ""; }; + 4874D59E1C221B9500A98102 /* AdaptiveBitrateControllerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AdaptiveBitrateControllerTest.swift; path = Tests/AdaptiveBitrate/AdaptiveBitrateControllerTest.swift; sourceTree = ""; }; + 4874D5A01C221CCF00A98102 /* R5ProTestbed-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "R5ProTestbed-Bridging-Header.h"; sourceTree = ""; }; + 4874D5A11C221CD000A98102 /* ALToastView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALToastView.h; sourceTree = ""; }; + 4874D5A21C221CD000A98102 /* ALToastView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALToastView.m; sourceTree = ""; }; + 4874ECCF1C483FDA002BDA6D /* SubscribeStreamImageTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeStreamImageTest.swift; path = Tests/SubscribeStreamImage/SubscribeStreamImageTest.swift; sourceTree = ""; }; + 48BB1D801CED140D00D915E5 /* SubscribeSetSizeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeSetSizeTest.swift; sourceTree = ""; }; + 48BF80AD1C245AA7000E996E /* PublishOrientationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishOrientationTest.swift; path = Tests/PublishOrientation/PublishOrientationTest.swift; sourceTree = ""; }; + 48BF80B01C245D29000E996E /* Info-100.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Info-100.png"; sourceTree = ""; }; + 48BF80B21C246F6D000E996E /* SubscribeAspectRatioTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeAspectRatioTest.swift; path = Tests/SubscribeAspectRatio/SubscribeAspectRatioTest.swift; sourceTree = ""; }; + 48DFEDA81C21BBC60040B624 /* R5ProTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = R5ProTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 48DFEDAB1C21BBC60040B624 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 48DFEDAD1C21BBC60040B624 /* MasterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = ""; }; + 48DFEDAF1C21BBC60040B624 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; + 48DFEDB21C21BBC60040B624 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 48DFEDB41C21BBC60040B624 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 48DFEDB71C21BBC60040B624 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 48DFEDB91C21BBC60040B624 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 48DFEDC51C21BBDB0040B624 /* R5Streaming.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = R5Streaming.framework; path = R5ProTestbed/R5Streaming.framework; sourceTree = ""; }; + 48DFEDCC1C21BF0A0040B624 /* PublishTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishTest.swift; path = Tests/Publish/PublishTest.swift; sourceTree = ""; }; + 48DFEDCE1C21BF2D0040B624 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 48DFEDD01C21BF320040B624 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 48DFEDD21C21BF370040B624 /* 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; }; + 48DFEDD41C21C18F0040B624 /* Home.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Home.swift; sourceTree = ""; }; + 48DFEDD61C21C2540040B624 /* BaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BaseTest.swift; path = Tests/BaseTest.swift; sourceTree = ""; }; + 48DFEDD81C21C5940040B624 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; + 48DFEDDA1C21C5AB0040B624 /* libstdc++.6.0.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.6.0.9.tbd"; path = "usr/lib/libstdc++.6.0.9.tbd"; sourceTree = SDKROOT; }; + 48DFEDDC1C21E91E0040B624 /* tests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = tests.plist; sourceTree = ""; }; + 48DFEDDE1C21EA780040B624 /* Testbed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Testbed.swift; sourceTree = ""; }; + 926794F41D061900004B0BCD /* PublishRemoteCallTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishRemoteCallTest.swift; path = Tests/RemoteCall/PublishRemoteCallTest.swift; sourceTree = ""; }; + 926794F61D06191E004B0BCD /* PublishStreamManagerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishStreamManagerTest.swift; path = Tests/PublishStreamManager/PublishStreamManagerTest.swift; sourceTree = ""; }; + 926794F81D061942004B0BCD /* RecordedTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RecordedTest.swift; path = Tests/Recorded/RecordedTest.swift; sourceTree = ""; }; + 926794FA1D061968004B0BCD /* SubscribeCluster.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeCluster.swift; path = Tests/SubscribeCluster/SubscribeCluster.swift; sourceTree = ""; }; + 926794FC1D061983004B0BCD /* SubscribeRemoteCallTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeRemoteCallTest.swift; path = Tests/RemoteCall/SubscribeRemoteCallTest.swift; sourceTree = ""; }; + 926794FE1D06199E004B0BCD /* TwoWayTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TwoWayTest.swift; path = Tests/TwoWay/TwoWayTest.swift; sourceTree = ""; }; + 928B1CBC1CBC0DC5002C66BA /* SubscribeStreamManagerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeStreamManagerTest.swift; path = Tests/SubscribeStreamManager/SubscribeStreamManagerTest.swift; sourceTree = ""; }; + 92CC78081CB6F8DD00CD4352 /* SubscribeTwoStreams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeTwoStreams.swift; path = Tests/SubscribeTwoStreams/SubscribeTwoStreams.swift; sourceTree = ""; }; + 92E13BF31CDD165800DA5783 /* SubscribeNoViewTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeNoViewTest.swift; path = Tests/SubscribeNoView/SubscribeNoViewTest.swift; sourceTree = ""; }; + 92EA19D91CE13717006B3A97 /* PublishCustomSourceTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PublishCustomSourceTest.swift; path = Tests/PublishCustomSource/PublishCustomSourceTest.swift; sourceTree = ""; }; + 92EA19DC1CE137DD006B3A97 /* CustomVideoSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomVideoSource.swift; path = Tests/PublishCustomSource/CustomVideoSource.swift; sourceTree = ""; }; + 92EAA9DA1D0734EA00B2CE46 /* SubscribeAutoReconnectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SubscribeAutoReconnectTest.swift; path = Tests/SubscribeReconnect/SubscribeAutoReconnectTest.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 48DFEDA51C21BBC60040B624 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 48DFEDDB1C21C5AB0040B624 /* libstdc++.6.0.9.tbd in Frameworks */, + 48DFEDD91C21C5940040B624 /* OpenAL.framework in Frameworks */, + 48DFEDD31C21BF370040B624 /* libiconv.2.4.0.tbd in Frameworks */, + 48DFEDD11C21BF320040B624 /* QuartzCore.framework in Frameworks */, + 48DFEDCF1C21BF2D0040B624 /* CoreFoundation.framework in Frameworks */, + 92F9A0681D020BA100F49644 /* R5Streaming.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 48BF80AF1C245D0B000E996E /* Resources */ = { + isa = PBXGroup; + children = ( + 48BF80B01C245D29000E996E /* Info-100.png */, + ); + name = Resources; + sourceTree = ""; + }; + 48DFED9F1C21BBC60040B624 = { + isa = PBXGroup; + children = ( + 48DFEDDA1C21C5AB0040B624 /* libstdc++.6.0.9.tbd */, + 48DFEDD81C21C5940040B624 /* OpenAL.framework */, + 48DFEDD21C21BF370040B624 /* libiconv.2.4.0.tbd */, + 48DFEDD01C21BF320040B624 /* QuartzCore.framework */, + 48DFEDCE1C21BF2D0040B624 /* CoreFoundation.framework */, + 48DFEDC51C21BBDB0040B624 /* R5Streaming.framework */, + 48DFEDAA1C21BBC60040B624 /* R5ProTestbed */, + 48BF80AF1C245D0B000E996E /* Resources */, + 48DFEDA91C21BBC60040B624 /* Products */, + ); + sourceTree = ""; + }; + 48DFEDA91C21BBC60040B624 /* Products */ = { + isa = PBXGroup; + children = ( + 48DFEDA81C21BBC60040B624 /* R5ProTestbed.app */, + ); + name = Products; + sourceTree = ""; + }; + 48DFEDAA1C21BBC60040B624 /* R5ProTestbed */ = { + isa = PBXGroup; + children = ( + 92EA19DB1CE1375A006B3A97 /* Utilities */, + 4874D5A11C221CD000A98102 /* ALToastView.h */, + 4874D5A21C221CD000A98102 /* ALToastView.m */, + 48DFEDD41C21C18F0040B624 /* Home.swift */, + 48DFEDC91C21BE0B0040B624 /* Tests */, + 48DFEDAB1C21BBC60040B624 /* AppDelegate.swift */, + 48DFEDAD1C21BBC60040B624 /* MasterViewController.swift */, + 48DFEDAF1C21BBC60040B624 /* DetailViewController.swift */, + 48DFEDB11C21BBC60040B624 /* Main.storyboard */, + 48DFEDB41C21BBC60040B624 /* Assets.xcassets */, + 48DFEDB61C21BBC60040B624 /* LaunchScreen.storyboard */, + 48DFEDB91C21BBC60040B624 /* Info.plist */, + 48DFEDDC1C21E91E0040B624 /* tests.plist */, + 48DFEDDE1C21EA780040B624 /* Testbed.swift */, + 4874D5A01C221CCF00A98102 /* R5ProTestbed-Bridging-Header.h */, + ); + path = R5ProTestbed; + sourceTree = ""; + }; + 48DFEDC91C21BE0B0040B624 /* Tests */ = { + isa = PBXGroup; + children = ( + 48BB1D801CED140D00D915E5 /* SubscribeSetSizeTest.swift */, + 48DFEDD61C21C2540040B624 /* BaseTest.swift */, + 48DFEDCC1C21BF0A0040B624 /* PublishTest.swift */, + 4874D59E1C221B9500A98102 /* AdaptiveBitrateControllerTest.swift */, + 485345081C23552F00D409F3 /* CameraSwapTest.swift */, + 92EA19D91CE13717006B3A97 /* PublishCustomSourceTest.swift */, + 485345061C23528400D409F3 /* PublishStreamImageTest.swift */, + 48BF80AD1C245AA7000E996E /* PublishOrientationTest.swift */, + 926794F81D061942004B0BCD /* RecordedTest.swift */, + 926794F41D061900004B0BCD /* PublishRemoteCallTest.swift */, + 926794F61D06191E004B0BCD /* PublishStreamManagerTest.swift */, + 4874D59C1C2216DB00A98102 /* SubscribeTest.swift */, + 48BF80B21C246F6D000E996E /* SubscribeAspectRatioTest.swift */, + 926794FA1D061968004B0BCD /* SubscribeCluster.swift */, + 92E13BF31CDD165800DA5783 /* SubscribeNoViewTest.swift */, + 92EAA9DA1D0734EA00B2CE46 /* SubscribeAutoReconnectTest.swift */, + 926794FC1D061983004B0BCD /* SubscribeRemoteCallTest.swift */, + 4874ECCF1C483FDA002BDA6D /* SubscribeStreamImageTest.swift */, + 928B1CBC1CBC0DC5002C66BA /* SubscribeStreamManagerTest.swift */, + 92CC78081CB6F8DD00CD4352 /* SubscribeTwoStreams.swift */, + 926794FE1D06199E004B0BCD /* TwoWayTest.swift */, + ); + name = Tests; + sourceTree = ""; + }; + 92EA19DB1CE1375A006B3A97 /* Utilities */ = { + isa = PBXGroup; + children = ( + 92EA19DC1CE137DD006B3A97 /* CustomVideoSource.swift */, + ); + name = Utilities; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 48DFEDA71C21BBC60040B624 /* R5ProTestbed */ = { + isa = PBXNativeTarget; + buildConfigurationList = 48DFEDBC1C21BBC60040B624 /* Build configuration list for PBXNativeTarget "R5ProTestbed" */; + buildPhases = ( + 48DFEDA41C21BBC60040B624 /* Sources */, + 48DFEDA51C21BBC60040B624 /* Frameworks */, + 48DFEDA61C21BBC60040B624 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = R5ProTestbed; + productName = R5ProTestbed; + productReference = 48DFEDA81C21BBC60040B624 /* R5ProTestbed.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 48DFEDA01C21BBC60040B624 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = Infrared5; + TargetAttributes = { + 48DFEDA71C21BBC60040B624 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = 48DFEDA31C21BBC60040B624 /* Build configuration list for PBXProject "R5ProTestbed" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 48DFED9F1C21BBC60040B624; + productRefGroup = 48DFEDA91C21BBC60040B624 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 48DFEDA71C21BBC60040B624 /* R5ProTestbed */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 48DFEDA61C21BBC60040B624 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 48DFEDDD1C21E91E0040B624 /* tests.plist in Resources */, + 48DFEDB81C21BBC60040B624 /* LaunchScreen.storyboard in Resources */, + 48DFEDB51C21BBC60040B624 /* Assets.xcassets in Resources */, + 48BF80B11C245D29000E996E /* Info-100.png in Resources */, + 48DFEDB31C21BBC60040B624 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 48DFEDA41C21BBC60040B624 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 48DFEDB01C21BBC60040B624 /* DetailViewController.swift in Sources */, + 48BF80B31C246F6D000E996E /* SubscribeAspectRatioTest.swift in Sources */, + 4874D5A31C221CD000A98102 /* ALToastView.m in Sources */, + 48DFEDAE1C21BBC60040B624 /* MasterViewController.swift in Sources */, + 92CC78091CB6F8DD00CD4352 /* SubscribeTwoStreams.swift in Sources */, + 92E13BF41CDD165800DA5783 /* SubscribeNoViewTest.swift in Sources */, + 926794FF1D06199E004B0BCD /* TwoWayTest.swift in Sources */, + 48DFEDD51C21C18F0040B624 /* Home.swift in Sources */, + 926794FD1D061983004B0BCD /* SubscribeRemoteCallTest.swift in Sources */, + 4874D59F1C221B9500A98102 /* AdaptiveBitrateControllerTest.swift in Sources */, + 48DFEDAC1C21BBC60040B624 /* AppDelegate.swift in Sources */, + 48DFEDDF1C21EA780040B624 /* Testbed.swift in Sources */, + 48BF80AE1C245AA7000E996E /* PublishOrientationTest.swift in Sources */, + 92EA19DD1CE137DD006B3A97 /* CustomVideoSource.swift in Sources */, + 926794FB1D061968004B0BCD /* SubscribeCluster.swift in Sources */, + 926794F51D061900004B0BCD /* PublishRemoteCallTest.swift in Sources */, + 4874ECD01C483FDA002BDA6D /* SubscribeStreamImageTest.swift in Sources */, + 485345091C23552F00D409F3 /* CameraSwapTest.swift in Sources */, + 92EA19DA1CE13717006B3A97 /* PublishCustomSourceTest.swift in Sources */, + 48DFEDD71C21C2540040B624 /* BaseTest.swift in Sources */, + 926794F91D061942004B0BCD /* RecordedTest.swift in Sources */, + 48DFEDCD1C21BF0A0040B624 /* PublishTest.swift in Sources */, + 92EAA9DB1D0734EA00B2CE46 /* SubscribeAutoReconnectTest.swift in Sources */, + 928B1CBD1CBC0DC5002C66BA /* SubscribeStreamManagerTest.swift in Sources */, + 4874D59D1C2216DB00A98102 /* SubscribeTest.swift in Sources */, + 48BB1D811CED140D00D915E5 /* SubscribeSetSizeTest.swift in Sources */, + 485345071C23528400D409F3 /* PublishStreamImageTest.swift in Sources */, + 926794F71D06191E004B0BCD /* PublishStreamManagerTest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 48DFEDB11C21BBC60040B624 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 48DFEDB21C21BBC60040B624 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 48DFEDB61C21BBC60040B624 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 48DFEDB71C21BBC60040B624 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 48DFEDBA1C21BBC60040B624 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 48DFEDBB1C21BBC60040B624 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 48DFEDBD1C21BBC60040B624 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/R5ProTestbed", + ); + INFOPLIST_FILE = R5ProTestbed/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.infrared5.R5ProTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "R5ProTestbed/R5ProTestbed-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VALID_ARCHS = "armv7 armv7s arm64"; + }; + name = Debug; + }; + 48DFEDBE1C21BBC60040B624 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/R5ProTestbed", + ); + INFOPLIST_FILE = R5ProTestbed/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.infrared5.R5ProTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "R5ProTestbed/R5ProTestbed-Bridging-Header.h"; + VALID_ARCHS = "armv7 armv7s arm64"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 48DFEDA31C21BBC60040B624 /* Build configuration list for PBXProject "R5ProTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 48DFEDBA1C21BBC60040B624 /* Debug */, + 48DFEDBB1C21BBC60040B624 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 48DFEDBC1C21BBC60040B624 /* Build configuration list for PBXNativeTarget "R5ProTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 48DFEDBD1C21BBC60040B624 /* Debug */, + 48DFEDBE1C21BBC60040B624 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 48DFEDA01C21BBC60040B624 /* Project object */; +} diff --git a/Red5ProStreaming.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/R5ProTestbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 67% rename from Red5ProStreaming.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to R5ProTestbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata index d28ac19..919434a 100644 --- a/Red5ProStreaming.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/R5ProTestbed.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/R5ProTestbed.xcodeproj/project.xcworkspace/xcshareddata/R5ProTestbed.xcscmblueprint b/R5ProTestbed.xcodeproj/project.xcworkspace/xcshareddata/R5ProTestbed.xcscmblueprint new file mode 100644 index 0000000..3dd32d5 --- /dev/null +++ b/R5ProTestbed.xcodeproj/project.xcworkspace/xcshareddata/R5ProTestbed.xcscmblueprint @@ -0,0 +1,30 @@ +{ + "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "DC7F46286BABB1E7799F2C699656855D2AE74077", + "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { + + }, + "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { + "DC7F46286BABB1E7799F2C699656855D2AE74077" : 0, + "15C55C8AB61EA81651E461AB17DC0E083B9C3718" : 0 + }, + "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "6C0F4670-3F5B-4F41-933B-E0E7F0DA8FF3", + "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { + "DC7F46286BABB1E7799F2C699656855D2AE74077" : "ios-streaming-testbed\/", + "15C55C8AB61EA81651E461AB17DC0E083B9C3718" : "red5pro-core-streaming-library\/" + }, + "DVTSourceControlWorkspaceBlueprintNameKey" : "R5ProTestbed", + "DVTSourceControlWorkspaceBlueprintVersion" : 204, + "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "R5ProTestbed.xcodeproj", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/infrared5\/red5pro-core-streaming-library.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "15C55C8AB61EA81651E461AB17DC0E083B9C3718" + }, + { + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/infrared5\/ios-streaming-testbed.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "DC7F46286BABB1E7799F2C699656855D2AE74077" + } + ] +} \ No newline at end of file diff --git a/R5ProTestbed.xcodeproj/xcshareddata/xcschemes/R5ProTestbed.xcscheme b/R5ProTestbed.xcodeproj/xcshareddata/xcschemes/R5ProTestbed.xcscheme new file mode 100644 index 0000000..c154138 --- /dev/null +++ b/R5ProTestbed.xcodeproj/xcshareddata/xcschemes/R5ProTestbed.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Red5ProStreaming/ALToastView/ALToastView.h b/R5ProTestbed/ALToastView.h old mode 100644 new mode 100755 similarity index 99% rename from Red5ProStreaming/ALToastView/ALToastView.h rename to R5ProTestbed/ALToastView.h index 16e6be2..060e7c4 --- a/Red5ProStreaming/ALToastView/ALToastView.h +++ b/R5ProTestbed/ALToastView.h @@ -33,4 +33,4 @@ + (void)toastInView:(UIView *)parentView withText:(NSString *)text; -@end \ No newline at end of file +@end diff --git a/Red5ProStreaming/ALToastView/ALToastView.m b/R5ProTestbed/ALToastView.m old mode 100644 new mode 100755 similarity index 76% rename from Red5ProStreaming/ALToastView/ALToastView.m rename to R5ProTestbed/ALToastView.m index f10f6dd..e9a1bf2 --- a/Red5ProStreaming/ALToastView/ALToastView.m +++ b/R5ProTestbed/ALToastView.m @@ -92,7 +92,7 @@ - (id)initWithText:(NSString *)text { + (void)toastInView:(UIView *)parentView withText:(NSString *)text { // Add new instance to queue ALToastView *view = [[ALToastView alloc] initWithText:text]; - + CGFloat lWidth = view.textLabel.frame.size.width; CGFloat lHeight = view.textLabel.frame.size.height; CGFloat pWidth = parentView.bounds.size.width; @@ -110,7 +110,7 @@ + (void)toastInView:(UIView *)parentView withText:(NSString *)text { else { [toasts addObject:view]; } - + } @@ -119,32 +119,32 @@ + (void)toastInView:(UIView *)parentView withText:(NSString *)text { - (void)fadeToastOut { // Fade in parent view - [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction - - animations:^{ - self.alpha = 0.f; + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction + + animations:^{ + self.alpha = 0.f; + } + completion:^(BOOL finished){ + UIView *parentView = self.superview; + [self removeFromSuperview]; + + // Remove current view from array + [toasts removeObject:self]; + if ([toasts count] == 0) { + + toasts = nil; } - completion:^(BOOL finished){ - UIView *parentView = self.superview; - [self removeFromSuperview]; - - // Remove current view from array - [toasts removeObject:self]; - if ([toasts count] == 0) { - - toasts = nil; - } - else - [ALToastView nextToastInView:parentView]; - }]; + else + [ALToastView nextToastInView:parentView]; + }]; } + (void)nextToastInView:(UIView *)parentView { if ([toasts count] > 0) { - ALToastView *view = [toasts objectAtIndex:0]; - - // NSLog(@"Showing toast with text: %@", view.textLabel.text); + ALToastView *view = [toasts objectAtIndex:0]; + + // NSLog(@"Showing toast with text: %@", view.textLabel.text); CGFloat lWidth = view.textLabel.frame.size.width; CGFloat lHeight = view.textLabel.frame.size.height; @@ -153,20 +153,20 @@ + (void)nextToastInView:(UIView *)parentView { // Change toastview frame view.frame = CGRectMake((pWidth - lWidth - 20) / 2., pHeight - lHeight - 60, lWidth + 20, lHeight + 10); - + // Fade into parent view [parentView addSubview:view]; - [UIView animateWithDuration:.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction - animations:^{ - view.alpha = 1.0; - [parentView updateConstraints]; - [parentView layoutIfNeeded]; - - } completion:^(BOOL finished){}]; - - // Start timer for fade out - [view performSelector:@selector(fadeToastOut) withObject:nil afterDelay:kDuration]; - } + [UIView animateWithDuration:.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction + animations:^{ + view.alpha = 1.0; + [parentView updateConstraints]; + [parentView layoutIfNeeded]; + + } completion:^(BOOL finished){}]; + + // Start timer for fade out + [view performSelector:@selector(fadeToastOut) withObject:nil afterDelay:kDuration]; + } } -@end \ No newline at end of file +@end diff --git a/R5ProTestbed/AppDelegate.swift b/R5ProTestbed/AppDelegate.swift new file mode 100644 index 0000000..a28822a --- /dev/null +++ b/R5ProTestbed/AppDelegate.swift @@ -0,0 +1,71 @@ +// +// AppDelegate.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + + + + + let t = Testbed.sharedInstance + + let d = Testbed.dictionary + NSLog((Testbed.testAtIndex(0)?.description)!) + //NSLog(Testbed.sharedInstance.testWithId("publish")!.description) + + let splitViewController = self.window!.rootViewController as! UISplitViewController + let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController + navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem() + splitViewController.delegate = self + return true + } + + func applicationWillResignActive(application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + // MARK: - Split view + + func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool { + guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false } + guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false } + if topAsDetailController.detailItem == nil { + // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded. + return true + } + return false + } + +} + diff --git a/Red5ProStreaming/Images.xcassets/AppIcon.appiconset/Contents.json b/R5ProTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 92% rename from Red5ProStreaming/Images.xcassets/AppIcon.appiconset/Contents.json rename to R5ProTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json index 36d2c80..eeea76c 100644 --- a/Red5ProStreaming/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/R5ProTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -59,6 +59,11 @@ "idiom" : "ipad", "size" : "76x76", "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" } ], "info" : { diff --git a/R5ProTestbed/Base.lproj/LaunchScreen.storyboard b/R5ProTestbed/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..90d6157 --- /dev/null +++ b/R5ProTestbed/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/R5ProTestbed/Base.lproj/Main.storyboard b/R5ProTestbed/Base.lproj/Main.storyboard new file mode 100644 index 0000000..852c0ee --- /dev/null +++ b/R5ProTestbed/Base.lproj/Main.storyboard @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/R5ProTestbed/DetailViewController.swift b/R5ProTestbed/DetailViewController.swift new file mode 100644 index 0000000..0a49c63 --- /dev/null +++ b/R5ProTestbed/DetailViewController.swift @@ -0,0 +1,147 @@ +// +// DetailViewController.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +class DetailViewController: UIViewController, UITextFieldDelegate { + + @IBOutlet weak var hostText: UITextField! + @IBOutlet weak var stream1Text: UITextField! + @IBOutlet weak var stream2Text: UITextField! + @IBOutlet weak var debugSwitch: UISwitch! + @IBOutlet weak var videoSwitch: UISwitch! + @IBOutlet weak var audioSwitch: UISwitch! + + var r5ViewController : BaseTest? = nil + + var detailItem: NSDictionary? { + didSet { + // Update the view. + // self.configureView() + } + } + + @IBAction func onStream1NameChange(sender: AnyObject) { + Testbed.setStream1Name(stream1Text.text!) + } + @IBAction func onStream2NameChange(sender: AnyObject) { + Testbed.setStream2Name(stream2Text.text!) + } + @IBAction func onStreamNameSwap(sender: AnyObject) { + Testbed.setStream1Name(stream2Text.text!) + Testbed.setStream2Name(stream1Text.text!) + stream1Text.text = Testbed.parameters!["stream1"] as? String + stream2Text.text = Testbed.parameters!["stream2"] as? String + } + @IBAction func onHostChange(sender: AnyObject) { + Testbed.setHost(hostText.text!) + } + @IBAction func onDebugChange( sender: AnyObject) { + Testbed.setDebug(debugSwitch.on) + } + @IBAction func onVideoChange( sender: AnyObject) { + Testbed.setVideo(videoSwitch.on) + } + @IBAction func onAudioChange( sender: AnyObject) { + Testbed.setAudio(audioSwitch.on) + } + + func configureView() { + // Update the user interface for the detail item. + + + hostText.text = Testbed.parameters!["host"] as? String + stream1Text.text = Testbed.parameters!["stream1"] as? String + stream2Text.text = Testbed.parameters!["stream2"] as? String + + hostText.delegate = self + stream1Text.delegate = self + stream2Text.delegate = self + + debugSwitch.setOn((Testbed.parameters!["debug_view"] as? Bool)!, animated: false) + videoSwitch.setOn((Testbed.parameters!["video_on"] as? Bool)!, animated: false) + audioSwitch.setOn((Testbed.parameters!["audio_on"] as? Bool)!, animated: false) + + if(self.detailItem != nil){ + + if(self.detailItem!["description"] != nil){ + + let navButton = UIBarButtonItem(title: "Info", style: UIBarButtonItemStyle.Plain, target: self, action: "showInfo") + navButton.imageInsets = UIEdgeInsetsMake(10, 10, 10, 10); + + navigationItem.rightBarButtonItem = navButton + } + + Testbed.setLocalOverrides(self.detailItem!["LocalProperties"] as? NSMutableDictionary) + + + let className = self.detailItem!["class"] as! String + let mClass = NSClassFromString(className) as! BaseTest.Type; + + //only add this view if it isn't HOME + if(!(mClass is Home.Type)){ + r5ViewController = mClass.init() + + self.addChildViewController(r5ViewController!) + self.view.addSubview(r5ViewController!.view) + + r5ViewController!.view.autoresizesSubviews = true + r5ViewController!.view.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth]; + } + + } + + + } + + func showInfo(){ + let alert = UIAlertView() + alert.title = "Info" + alert.message = self.detailItem!["description"] as? String + alert.addButtonWithTitle("OK") + alert.show() + + + } + + func textFieldShouldReturn(textField: UITextField) -> Bool { + self.view.endEditing(true) + return false + } + + + override func viewWillDisappear(animated: Bool) { + + closeCurrentTest() + } + + func closeCurrentTest(){ + + if(r5ViewController != nil){ + r5ViewController!.closeTest() + } + r5ViewController = nil + + } + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + self.configureView() + self.view.autoresizesSubviews = true + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/R5ProTestbed/Home.swift b/R5ProTestbed/Home.swift new file mode 100644 index 0000000..b711001 --- /dev/null +++ b/R5ProTestbed/Home.swift @@ -0,0 +1,14 @@ +// +// Home.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit + +@objc(Home) +class Home: BaseTest { + +} diff --git a/Red5ProStreaming/Info.plist b/R5ProTestbed/Info.plist similarity index 87% rename from Red5ProStreaming/Info.plist rename to R5ProTestbed/Info.plist index 4686562..3b1db2c 100644 --- a/Red5ProStreaming/Info.plist +++ b/R5ProTestbed/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.infrared5.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -22,23 +22,28 @@ 1 LSRequiresIPhoneOS - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSCameraUsageDescription - Red5 Pro uses your camera for live streaming - NSMicrophoneUsageDescription - Red5 Pro uses your microphone for live streaming UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main + NSCameraUsageDescription + Red5 Pro uses your camera for live streaming + NSMicrophoneUsageDescription + Red5 Pro uses your microphone for live streaming UIRequiredDeviceCapabilities armv7 + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -52,5 +57,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + diff --git a/R5ProTestbed/MasterViewController.swift b/R5ProTestbed/MasterViewController.swift new file mode 100644 index 0000000..2f68ef2 --- /dev/null +++ b/R5ProTestbed/MasterViewController.swift @@ -0,0 +1,101 @@ +// +// MasterViewController.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit + +class MasterViewController: UITableViewController { + + var detailViewController: DetailViewController? = nil + + + let objects = [ + [ + ["Name": "Home", "class": Home.self], + ["Name": "Publish", "class": PublishTest.self] + ], + [ + ["Name": "Adaptive Bitrate", "class": Home.self] + ] + ] + + + + override func viewDidLoad() { + super.viewDidLoad() + self.splitViewController!.preferredPrimaryColumnWidthFraction = 0.2 + self.view.autoresizesSubviews = true + self.splitViewController!.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible + + // Do any additional setup after loading the view, typically from a nib. + // self.navigationItem.leftBarButtonItem = self.editButtonItem() + + //let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:") + //self.navigationItem.rightBarButtonItem = addButton + if let split = self.splitViewController { + let controllers = split.viewControllers + self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController + } + } + + override func viewWillAppear(animated: Bool) { + //self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed + super.viewWillAppear(animated) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + // MARK: - Segues + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + if segue.identifier == "showDetail" { + if let indexPath = self.tableView.indexPathForSelectedRow { + + let object = Testbed.testAtIndex(indexPath.row) + let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController + controller.detailItem = object + controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() + controller.navigationItem.leftItemsSupplementBackButton = true + } + } + } + + // MARK: - Table View + + + override func numberOfSectionsInTableView(tableView: UITableView) -> Int { + + return Testbed.sections() + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return Testbed.rowsInSection() + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) + + + cell.textLabel!.text = Testbed.testAtIndex(indexPath.row)?.valueForKey("name")?.description + return cell + } + + override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return false + } + + override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { + } + + +} + diff --git a/Red5ProStreaming/Red5ProStreaming-Bridging-Header.h b/R5ProTestbed/R5ProTestbed-Bridging-Header.h similarity index 63% rename from Red5ProStreaming/Red5ProStreaming-Bridging-Header.h rename to R5ProTestbed/R5ProTestbed-Bridging-Header.h index d265e2e..62c6bce 100644 --- a/Red5ProStreaming/Red5ProStreaming-Bridging-Header.h +++ b/R5ProTestbed/R5ProTestbed-Bridging-Header.h @@ -2,5 +2,5 @@ // Use this file to import your target's public headers that you would like to expose to Swift. // -#import "BaseExample.h" +#import "ALToastView.h" #import \ No newline at end of file diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/AVEncoder.h b/R5ProTestbed/R5Streaming.framework/Headers/AVEncoder.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/AVEncoder.h rename to R5ProTestbed/R5Streaming.framework/Headers/AVEncoder.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5AdaptiveBitrateController.h b/R5ProTestbed/R5Streaming.framework/Headers/R5AdaptiveBitrateController.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5AdaptiveBitrateController.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5AdaptiveBitrateController.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5AudioController.h b/R5ProTestbed/R5Streaming.framework/Headers/R5AudioController.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5AudioController.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5AudioController.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5Camera.h b/R5ProTestbed/R5Streaming.framework/Headers/R5Camera.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5Camera.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5Camera.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5Configuration.h b/R5ProTestbed/R5Streaming.framework/Headers/R5Configuration.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5Configuration.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5Configuration.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5Connection.h b/R5ProTestbed/R5Streaming.framework/Headers/R5Connection.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5Connection.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5Connection.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5Stream.h b/R5ProTestbed/R5Streaming.framework/Headers/R5Stream.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5Stream.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5Stream.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5Streaming.h b/R5ProTestbed/R5Streaming.framework/Headers/R5Streaming.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5Streaming.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5Streaming.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5VideoSource.h b/R5ProTestbed/R5Streaming.framework/Headers/R5VideoSource.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5VideoSource.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5VideoSource.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/R5VideoViewController.h b/R5ProTestbed/R5Streaming.framework/Headers/R5VideoViewController.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/R5VideoViewController.h rename to R5ProTestbed/R5Streaming.framework/Headers/R5VideoViewController.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/codec_facade.h b/R5ProTestbed/R5Streaming.framework/Headers/codec_facade.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/codec_facade.h rename to R5ProTestbed/R5Streaming.framework/Headers/codec_facade.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/connection.h b/R5ProTestbed/R5Streaming.framework/Headers/connection.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/connection.h rename to R5ProTestbed/R5Streaming.framework/Headers/connection.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/global.h b/R5ProTestbed/R5Streaming.framework/Headers/global.h similarity index 98% rename from Red5ProStreaming/R5Streaming.framework/Headers/global.h rename to R5ProTestbed/R5Streaming.framework/Headers/global.h index 6fcf540..d94f5f4 100644 --- a/Red5ProStreaming/R5Streaming.framework/Headers/global.h +++ b/R5ProTestbed/R5Streaming.framework/Headers/global.h @@ -20,9 +20,9 @@ extern "C" { #define STRINGIFY_(s) #s #define STRINGIFY(s) STRINGIFY_(s) - -#define R5PRO_MAJOR_VERSION 2 -#define R5PRO_MINOR_VERSION 0 + +#define R5PRO_MAJOR_VERSION 1 +#define R5PRO_MINOR_VERSION 3 #define R5PRO_REVISION 0 #define R5PRO_BUILD 0 diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/parse_util.h b/R5ProTestbed/R5Streaming.framework/Headers/parse_util.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/parse_util.h rename to R5ProTestbed/R5Streaming.framework/Headers/parse_util.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/publish.h b/R5ProTestbed/R5Streaming.framework/Headers/publish.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/publish.h rename to R5ProTestbed/R5Streaming.framework/Headers/publish.h diff --git a/Red5ProStreaming/R5Streaming.framework/Headers/session_description.h b/R5ProTestbed/R5Streaming.framework/Headers/session_description.h similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Headers/session_description.h rename to R5ProTestbed/R5Streaming.framework/Headers/session_description.h diff --git a/Red5ProStreaming/R5Streaming.framework/Info.plist b/R5ProTestbed/R5Streaming.framework/Info.plist similarity index 54% rename from Red5ProStreaming/R5Streaming.framework/Info.plist rename to R5ProTestbed/R5Streaming.framework/Info.plist index 2b6cb73..0b7b62b 100644 Binary files a/Red5ProStreaming/R5Streaming.framework/Info.plist and b/R5ProTestbed/R5Streaming.framework/Info.plist differ diff --git a/Red5ProStreaming/R5Streaming.framework/Modules/module.modulemap b/R5ProTestbed/R5Streaming.framework/Modules/module.modulemap similarity index 100% rename from Red5ProStreaming/R5Streaming.framework/Modules/module.modulemap rename to R5ProTestbed/R5Streaming.framework/Modules/module.modulemap diff --git a/Red5ProStreaming/R5Streaming.framework/R5Streaming b/R5ProTestbed/R5Streaming.framework/R5Streaming similarity index 85% rename from Red5ProStreaming/R5Streaming.framework/R5Streaming rename to R5ProTestbed/R5Streaming.framework/R5Streaming index 93f349b..2c36f82 100644 Binary files a/Red5ProStreaming/R5Streaming.framework/R5Streaming and b/R5ProTestbed/R5Streaming.framework/R5Streaming differ diff --git a/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeDirectory b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeDirectory new file mode 100644 index 0000000..164090f Binary files /dev/null and b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeDirectory differ diff --git a/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeRequirements b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeRequirements new file mode 100644 index 0000000..9eb9188 Binary files /dev/null and b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeRequirements differ diff --git a/Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeResources b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeResources similarity index 56% rename from Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeResources rename to R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeResources index 4c0e829..0baed34 100644 --- a/Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeResources +++ b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeResources @@ -54,7 +54,7 @@ Headers/global.h - pI2iWTxObmMJAJ4dzgoQak8Tre8= + 5Dp8AMCiFyxZqm9MS0vw+fWT8lY= Headers/parse_util.h @@ -70,7 +70,7 @@ Info.plist - XXTs7XisfDJQByNKnN6xI8SdwAc= + cGNA208Y+mZqox22Ki9kbYHhqVY= Modules/module.modulemap @@ -80,192 +80,73 @@ files2 Headers/AVEncoder.h - - hash - - 8Log7+R7VxvLOYPBz3r6SUn0oBA= - - hash2 - - cFG17s09Ph4iII5M/A6mY4ykeJnFyY4nxgJvTDD9Vt4= - - + + 8Log7+R7VxvLOYPBz3r6SUn0oBA= + Headers/R5AdaptiveBitrateController.h - - hash - - zkFmrYT2RgXT+k0nJjfaf3anZDg= - - hash2 - - 3HxkVJPZTr9XMgKHRm6TfyzxpFZNB1OA67FvX2RWTSk= - - + + zkFmrYT2RgXT+k0nJjfaf3anZDg= + Headers/R5AudioController.h - - hash - - RENWHBimfMpcu8ybcI7BwgLQ+rw= - - hash2 - - lyHsSjsoM1PVIL8s9KTrWapPAjSUfqE3+4kSvDeAzp4= - - + + RENWHBimfMpcu8ybcI7BwgLQ+rw= + Headers/R5Camera.h - - hash - - mRCApmkU+gc0t9raxiydaea2ZIQ= - - hash2 - - hMHxyJmxFeY+4xERDXGGRaiHju1kphQb/byy2utaeV8= - - + + mRCApmkU+gc0t9raxiydaea2ZIQ= + Headers/R5Configuration.h - - hash - - PGvXo+880HvO/HGzQzRgMw5yhDU= - - hash2 - - QiEprpIWI43BEytmg8BU+4psAIHZHJJIKg/2ZkriRbM= - - + + PGvXo+880HvO/HGzQzRgMw5yhDU= + Headers/R5Connection.h - - hash - - 318NWc1x+HDhhglr7LvCzLETT1M= - - hash2 - - i+O7cC53so74aIA6iHa8GumZq9paBOfp9ly3t2B5vZk= - - + + 318NWc1x+HDhhglr7LvCzLETT1M= + Headers/R5Stream.h - - hash - - UodakIPdb/kKu0viJZm8wn4yV4g= - - hash2 - - WbOS5dDK6Ptyt7gkQzSxhzM19koyBdz98DOwSKDLJCE= - - + + UodakIPdb/kKu0viJZm8wn4yV4g= + Headers/R5Streaming.h - - hash - - 5tMO9dkjFygcrpxRVB1YNsNujqI= - - hash2 - - Tw4Z8PdjDpbSTitvRzNo/oaoqL8w7d6iIfIJBobOV/g= - - + + 5tMO9dkjFygcrpxRVB1YNsNujqI= + Headers/R5VideoSource.h - - hash - - d9iBl/se74pzy3zt9W7c356KegQ= - - hash2 - - gfVRKqJd4Am5aaCjheeLKIgYusswlGnHI1o8Eepl+HQ= - - + + d9iBl/se74pzy3zt9W7c356KegQ= + Headers/R5VideoViewController.h - - hash - - DeVd0/m0/2Q2kgpHTxiklagoBuA= - - hash2 - - xteNIWcwbR14oHehX6MRNv248vqPcqn0+LWTAEQEpt0= - - + + DeVd0/m0/2Q2kgpHTxiklagoBuA= + Headers/codec_facade.h - - hash - - I4bc/dY/Vk0Hp7Q5FZ7uH50sgwo= - - hash2 - - aDB4dOs+nVvM0k9piC/N20g+AeYSs3678wHpTR8IFKU= - - + + I4bc/dY/Vk0Hp7Q5FZ7uH50sgwo= + Headers/connection.h - - hash - - buT40SKAyKh5T8NU3JJL8/1LbcI= - - hash2 - - aeSRSev3jEq6OppEeDQBP+JGxjjnJr6jTrF7UMC8+g8= - - + + buT40SKAyKh5T8NU3JJL8/1LbcI= + Headers/global.h - - hash - - pI2iWTxObmMJAJ4dzgoQak8Tre8= - - hash2 - - 00Ik5WJZ/gjYV6Y5VjD9IaKZ/lf6nnn7kl9fz6y8mYE= - - + + 5Dp8AMCiFyxZqm9MS0vw+fWT8lY= + Headers/parse_util.h - - hash - - qim3pDyB769D7HIsKpi+kbLoo5g= - - hash2 - - qb0EZtoE9hj5C5KnFtDvC5VUqinnrAQOseGzB+EvUeg= - - + + qim3pDyB769D7HIsKpi+kbLoo5g= + Headers/publish.h - - hash - - GHAFWpgHOeKbClEH4Ow2L4KqW8k= - - hash2 - - 2ZqkxDvHdmevoiwEll1B4rl99AUu0Mz00W0gdfJQ8iA= - - + + GHAFWpgHOeKbClEH4Ow2L4KqW8k= + Headers/session_description.h - - hash - - ZNt7GqX4/fqSxN9XvTgNd01eAuA= - - hash2 - - hoWYAruhuNOTHxkHHmqf9Bf5HVOZxF75vPS1RUqmRww= - - + + ZNt7GqX4/fqSxN9XvTgNd01eAuA= + Modules/module.modulemap - - hash - - tOdhB530EnW1VsyDpGfQPObaxh4= - - hash2 - - uGUmiTtd5DDGwLrPyK1zdlwwSBue48L9b7SfgqmXg/w= - - + + tOdhB530EnW1VsyDpGfQPObaxh4= + rules diff --git a/Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeSignature b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeSignature similarity index 62% rename from Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeSignature rename to R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeSignature index ef3b7c5..eaa9cf0 100644 Binary files a/Red5ProStreaming/R5Streaming.framework/_CodeSignature/CodeSignature and b/R5ProTestbed/R5Streaming.framework/_CodeSignature/CodeSignature differ diff --git a/R5ProTestbed/SubscribeSetSizeTest.swift b/R5ProTestbed/SubscribeSetSizeTest.swift new file mode 100644 index 0000000..5f91b86 --- /dev/null +++ b/R5ProTestbed/SubscribeSetSizeTest.swift @@ -0,0 +1,70 @@ +// +// SubscribeAspectRatioTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/18/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeSetSizeTest) +class SubscribeSetSizeTest: BaseTest { + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + setupR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + + currentView?.attachStream(subscribeStream) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + } + + func setupR5VideoViewController() -> R5VideoViewController{ + + let r5View : R5VideoViewController = getNewR5VideoViewController(self.view.frame); + self.addChildViewController(r5View); + + view.addSubview(r5View.view) + + r5View.showPreview(true) + + r5View.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + currentView = r5View; + + r5View.setFrame(CGRect(x: 100, y: 100, width: 200, height: 200)); + + return currentView! + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + currentView!.setFrame(CGRect(x: 100, y: 100, width: 200, height: 200)); + + } + + override func viewDidLayoutSubviews() { + + + if(currentView != nil){ + + // currentView?.setFrame(view.frame); + } + + } + +} diff --git a/R5ProTestbed/Testbed.swift b/R5ProTestbed/Testbed.swift new file mode 100644 index 0000000..2277d29 --- /dev/null +++ b/R5ProTestbed/Testbed.swift @@ -0,0 +1,120 @@ +// +// Testbed.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit + +class Testbed: NSObject { + + static let sharedInstance = Testbed() + static var dictionary : NSMutableDictionary? + static var tests : Array? + static var parameters : NSMutableDictionary? + static var localParameters : NSMutableDictionary? + + override init() { + super.init() + + loadTests() + + NSLog(Testbed.dictionary!.description) + + } + + + static func sections()->Int{ + return 1 + } + + static func rowsInSection()->Int{ + + return (Testbed.tests?.count)! + } + + static func testAtIndex(index : Int)-> NSDictionary?{ + + return tests![index] + } + + + static func setHost(ip : String){ + Testbed.parameters?.setValue(ip, forKey: "host") + } + + static func setStreamName(name : String){ + Testbed.parameters?.setValue(name, forKey: "stream1") + } + + static func setStream1Name(name : String){ + Testbed.parameters?.setValue(name, forKey: "stream1") + } + + static func setStream2Name(name : String){ + Testbed.parameters?.setValue(name, forKey: "stream2") + } + + static func setDebug(on : Bool){ + Testbed.parameters?.setValue(on, forKey: "debug_view") + } + + static func setVideo(on : Bool){ + Testbed.parameters?.setValue(on, forKey: "video_on") + } + + static func setAudio(on : Bool){ + Testbed.parameters?.setValue(on, forKey: "audio_on") + } + + static func setLocalOverrides(params : NSMutableDictionary?){ + Testbed.localParameters = params + } + + static func getParameter(param : String)->AnyObject?{ + + if(Testbed.localParameters != nil){ + if(Testbed.localParameters?[param] != nil){ + return Testbed.localParameters?[param] + } + } + + return Testbed.parameters?[param] + } + + + func loadTests(){ + + let path = NSBundle.mainBundle().pathForResource("tests", ofType: "plist") + + Testbed.dictionary = NSMutableDictionary(contentsOfFile: path!)//readDictionaryFromFile(path!) + Testbed.tests = Array() + + for (_, myValue) in (Testbed.dictionary!.valueForKey("Tests") as? NSDictionary)! { + Testbed.tests?.append(myValue as! NSMutableDictionary) + + } + + + Testbed.tests!.sortInPlace({(dic1 : NSMutableDictionary, dic2 : NSMutableDictionary)->Bool in + + if(dic1["name"] as! String == "Home"){ + return true + }else if(dic2["name"] as! String == "Home"){ + return false + } + + return dic2["name"] as! String > dic1["name"] as! String + + }) + + Testbed.parameters = Testbed.dictionary!.valueForKey("GlobalProperties") as? NSMutableDictionary + + + } + + + +} diff --git a/R5ProTestbed/Tests/AdaptiveBitrate/AdaptiveBitrateControllerTest.swift b/R5ProTestbed/Tests/AdaptiveBitrate/AdaptiveBitrateControllerTest.swift new file mode 100644 index 0000000..2d82a78 --- /dev/null +++ b/R5ProTestbed/Tests/AdaptiveBitrate/AdaptiveBitrateControllerTest.swift @@ -0,0 +1,43 @@ +// +// AdaptiveBitrateControllerTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(AdaptiveBitrateControllerTest) +class AdaptiveBitrateControllerTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + //The Adaptive bitrate controller! + let controller = R5AdaptiveBitrateController() + controller.attachToStream(self.publishStream!) + controller.requiresVideo = Testbed.getParameter("video_on") as! Bool + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + + + } + +} diff --git a/R5ProTestbed/Tests/AdaptiveBitrate/README.md b/R5ProTestbed/Tests/AdaptiveBitrate/README.md new file mode 100644 index 0000000..93d9b5e --- /dev/null +++ b/R5ProTestbed/Tests/AdaptiveBitrate/README.md @@ -0,0 +1,36 @@ +#Adaptive Bitrate Publishing + +This example demonstrates the AdaptiveBitrateController, which provides a mechanism to dynamically adjust the video publishing bitrate to adjust quality to meet the bandwidth restrictions of the network connection or encoding hardware. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[AdaptiveBitrateControllerTest.swift](AdaptiveBitrateControllerTest.swift)*** + +###Setup +The AdaptiveBitrateController is easy to set up. You simply create a new instance of the controller and attach the stream you wish to control. It will monitor the stream and make all adjustments automatically for you. + + +```Swift +let controller = R5AdaptiveBitrateController() +controller.attachToStream(self.publishStream!) +``` + +[AdaptiveBitrateExample.swift #32](AdaptiveBitrateControllerTest.swift#L32) + + +The controller will continuously adjust the video bitrate until the stream has closed. + +###Range +The AdaptiveBitrateController will dynamically adjust the video bitrate between the lowest possible bitrate the encoder can encode at, and the value set on the R5VideoSource (typically an R5Camera) on the stream. In this case, the value is assigned in the base class according to the value in tests.plist + +```Swift +let camera = R5Camera(device: videoDevice, andBitRate: Int32(Testbed.getParameter("bitrate") as! Int)) +``` + +[BaseTest.swift #85](../BaseTest.swift#L85) + + + +The controller will adjust the bitrate ~200 kbps every 2 seconds to achieve the best possible video quality. + +Video will be turned off if the stream is unable to maintain a smooth connection at the lowest possible bitrate. You can force video to be included with the `AdaptiveBitrateController.requiresVideo` flag. \ No newline at end of file diff --git a/R5ProTestbed/Tests/BaseTest.swift b/R5ProTestbed/Tests/BaseTest.swift new file mode 100644 index 0000000..cfcab0f --- /dev/null +++ b/R5ProTestbed/Tests/BaseTest.swift @@ -0,0 +1,149 @@ +// +// BaseTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(BaseTest) +class BaseTest: UIViewController , R5StreamDelegate { + + func onR5StreamStatus(stream: R5Stream!, withStatus statusCode: Int32, withMessage msg: String!) { + NSLog("Status: %s ", r5_string_for_status(statusCode)) + let s = String(format: "Status: %s (%@)", r5_string_for_status(statusCode), msg) + + ALToastView.toastInView(self.view, withText:s) + } + + var currentView : R5VideoViewController? = nil + var publishStream : R5Stream? = nil + var subscribeStream : R5Stream? = nil + + required init () { + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + func closeTest(){ + + NSLog("closing view") + + if( self.publishStream != nil ){ + self.publishStream!.stop() + } + + if( self.subscribeStream != nil ){ + self.subscribeStream!.stop() + } + + self.removeFromParentViewController() + } + + func getConfig()->R5Configuration{ + // Set up the configuration + let config = R5Configuration() + config.host = Testbed.getParameter("host") as! String + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + return config + } + + + override func viewDidLayoutSubviews() { + + super.viewDidLayoutSubviews() + + if(currentView != nil){ + + currentView?.setFrame(view.frame); + } + + } + + func setupPublisher(connection: R5Connection){ + + self.publishStream = R5Stream(connection: connection) + self.publishStream!.delegate = self + + if(Testbed.getParameter("video_on") as! Bool){ + // Attach the video from camera to stream + let videoDevice = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo).last as? AVCaptureDevice + + let camera = R5Camera(device: videoDevice, andBitRate: Int32(Testbed.getParameter("bitrate") as! Int)) + camera.width = Int32(Testbed.getParameter("camera_width") as! Int) + camera.height = Int32(Testbed.getParameter("camera_height") as! Int) + camera.orientation = 90 + self.publishStream!.attachVideo(camera) + } + if(Testbed.getParameter("audio_on") as! Bool){ + // Attach the audio from microphone to stream + let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio) + let microphone = R5Microphone(device: audioDevice) + microphone.bitrate = 32 + microphone.device = audioDevice; + NSLog("Got device %@", audioDevice) + self.publishStream!.attachAudio(microphone) + } + + } + + override func viewDidLoad() { + super.viewDidLoad() + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + r5_set_log_level((Int32)(r5_log_level_debug.rawValue)) + + self.view.autoresizesSubviews = true + + } + + override func viewDidAppear(animated: Bool) { + + //this is just to have a white background to the example + let backView : UIView = UIView(frame: self.view.frame); + backView.backgroundColor = UIColor.whiteColor(); + self.view.addSubview(backView); + + } + + func setupDefaultR5VideoViewController() -> R5VideoViewController{ + + let r5View : R5VideoViewController = getNewR5VideoViewController(self.view.frame); + self.addChildViewController(r5View); + + + view.addSubview(r5View.view) + + r5View.showPreview(true) + + r5View.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + currentView = r5View; + + return currentView! + } + + func getNewR5VideoViewController(rect : CGRect) -> R5VideoViewController{ + + let view : UIView = UIView(frame: rect) + + let r5View : R5VideoViewController = R5VideoViewController(); + r5View.view = view; + + return r5View; + } + +} diff --git a/R5ProTestbed/Tests/CameraSwap/CameraSwapTest.swift b/R5ProTestbed/Tests/CameraSwap/CameraSwapTest.swift new file mode 100644 index 0000000..4d94aac --- /dev/null +++ b/R5ProTestbed/Tests/CameraSwap/CameraSwapTest.swift @@ -0,0 +1,78 @@ +// +// CameraSwapTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/17/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(CameraSwapTest) +class CameraSwapTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + //change which camera is being used!!! + + //get front and back camera!!!! + + var frontCamera : AVCaptureDevice? + var backCamera : AVCaptureDevice? + + for device in AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo){ + let device = device as! AVCaptureDevice + if frontCamera == nil && device.position == AVCaptureDevicePosition.Front { + frontCamera = device + continue; + }else if backCamera == nil && device.position == AVCaptureDevicePosition.Back{ + backCamera = device + } + + } + + let camera = self.publishStream?.getVideoSource() as! R5Camera + + if(camera.device === frontCamera){ + camera.device = backCamera; + }else{ + camera.device = frontCamera; + } + + + } + + +} diff --git a/R5ProTestbed/Tests/CameraSwap/README.md b/R5ProTestbed/Tests/CameraSwap/README.md new file mode 100644 index 0000000..39e9d27 --- /dev/null +++ b/R5ProTestbed/Tests/CameraSwap/README.md @@ -0,0 +1,25 @@ +#Publish Camera Swap + +`R5Camera.device` allows the user to change the video source for a stream without interupting the broadcast. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[CameraSwapTest.swift](CameraSwapTest.swift)*** + +##Running the example +Touch the screen at any time while streaming to switch between broadcasting from the front facing and back facing camera. + +##Using R5Camera.device +`R5Camera.device` is a reference to the device set in instantiation of the R5Camera object. By getting the instance of R5Camera attached to the R5Stream object and changeing its device property, you can hot-swap sources for the stream. Once streaming, simply call: + +```Swift +let camera = self.publishStream?.getVideoSource() as! R5Camera +if(camera.device === frontCamera){ + camera.device = backCamera; +}else{ + camera.device = frontCamera; +} +``` + +[CameraSwapTest.swift #66](CameraSwapTest.swift#L66) + \ No newline at end of file diff --git a/R5ProTestbed/Tests/Publish/PublishTest.swift b/R5ProTestbed/Tests/Publish/PublishTest.swift new file mode 100644 index 0000000..2e7fb8e --- /dev/null +++ b/R5ProTestbed/Tests/Publish/PublishTest.swift @@ -0,0 +1,43 @@ +// +// PublishTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(PublishTest) +class PublishTest: BaseTest { + + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + // self.publishStream?.getVideoSource().fps = 2; + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + + + } +} diff --git a/R5ProTestbed/Tests/Publish/README.md b/R5ProTestbed/Tests/Publish/README.md new file mode 100644 index 0000000..275f161 --- /dev/null +++ b/R5ProTestbed/Tests/Publish/README.md @@ -0,0 +1,159 @@ +#Publishing on Red5 Pro + +This is the basic starter example on publishing to a Red5 Pro stream. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[PublishTest.swift](PublishTest.swift)*** + + +##How to Publish +Publishing to a Red5 Pro stream requires a few components to function fully. +####Setup R5Connection +The R5Connection manages the connection that the stream utilizes. You will need to setup a configuration and intialize a new connection. + +``` +Swift +func getConfig()->R5Configuration{ + // Set up the configuration + let config = R5Configuration() + config.host = Testbed.getParameter("host") as! String + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + return config +} +``` + +[BaseTest.swift #50](../BaseTest.swift#L50) + + +``` +Swift +let config = getConfig() +// Set up the connection and stream +let connection = R5Connection(config: config) +``` + +[PublishTest.swift #27](PublishTest.swift#L27) + + +####Setup R5Stream +The `R5Stream` handles both subscribing and publishing. Creating one simply requires the connection already created. + +``` +Swift +//Create our new stream that will utilize that connection +self.publishStream = R5Stream(connection: connection) +//Setup our listener to handle events from this stream +self.publishStream!.delegate = self +``` + +[BaseTest.swift #75](../BaseTest.swift#L75) + + +The `R5StreamDelegate` that is assigned to the `R5Stream` will receive status events for that stream, including connecting, disconnecting, and errors. + +####Attach a Video Source +The R5Stream will need a video and/or audio source to stream from. To attach a video source, you will need to create an `R5Camera` with the `AVCaptureDevice` you wish to stream from. + +``` +Swift +//Use the last device in the list of available cameras +let videoDevice = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo).last as? AVCaptureDevice +//Create an R5Camera with that device and specify the max bitrate to allow +//Note : This bitrate will not be respected if it is lower than the encoder can go! +let camera = R5Camera(device: videoDevice, andBitRate: Int32(Testbed.getParameter("bitrate") as! Int)) +//Set up the resolution we want this camera to use. This can only be set before publishing begins +camera.width = 900 +camera.height = 600 +//Setup the rotation of the video stream. This is meta data, and is used by the client to rotate the video. No rotation is done on the publisher. +camera.orientation = 90 +//Add the camera to the stream +self.publishStream!.attachVideo(camera) +``` + +[BaseTest.swift #83](../BaseTest.swift#L83) + + +`R5Camera.width` and `R5Camera.height` specify the encoded video size to be streamed. `R5Camera` will choose the video format that is closest to this resolution from the camera. + +`R5Camera.orientation` provides meta information to the stream for presentation on the client. The video is not rotated by the device. A value of **90** will provide portrait orientation on receiving devices. + +####Attach an Audio Source +To add audio to a stream a `R5Microphone` object can be attached. It behaves similarly to `R5Camera`, but requires `R5Stream.attachAudio` instead. + +``` +Swift +//Setup a new R5Microphone for streaming audio with that device +let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio) +let microphone = R5Microphone(device: audioDevice) +microphone.bitrate = 32 +microphone.device = audioDevice; +NSLog("Got device %@", audioDevice) +//Attach the microphone to the stream +self.publishStream!.attachAudio(microphone) +``` + +[BaseTest.swift #92](../BaseTest.swift#L92) + + +#### Preview the Publisher +The `R5VideoViewController` will present publishing streams as well as subscribed streams. To preview a publishing stream, it simply needs to attach the `R5Stream`. + +***This is not required to publish - but allows for previewing the stream.*** + +An `R5VideoViewController` can be set on any UIViewController, or created programmatically + +``` +Swift +let r5View : R5VideoViewController = getNewR5VideoViewController(self.view.frame); +self.addChildViewController(r5View); +``` + +[BaseTest.swift #121](../BaseTest.swift#L121) + + +To view the preview before publishing has started, use `R5VideoViewController.showPreview`. + +``` +Swift +view.addSubview(r5View.view) +r5View.showPreview(true) +r5View.showDebugInfo(true) +``` + + +[BaseTest.swift #121](../BaseTest.swift#L121) + + +Lastly, we attach the Stream to the R5VideoView to see the streaming content. + +``` +Swift +self.currentView!.attachStream(publishStream!) +``` + +[PublishTest.swift #35](PublishTest.swift#L35) + + +####Start Publishing +The `R5Stream.publish` method will establish the server connection and begin publishing. + +``` +Swift +self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) +``` + +[PublishTest.swift #38](PublishTest.swift#L38) + + +The *type* parameter tells the server the recording mode to use on the server. + +- **R5RecordTypeLive** - Stream but do not record +- **R5RecordTypeRecord** - Stream and record the file name. Replace existing save. +- **R5RecordTypeAppend** - Stream and append the recording to any existing save. + +####View your stream +Open a browser window and navigate to http://your_red5_pro_server_ip:5080//live/subscribe.jsp to see a list of active streams. Click on the _flash version to subscribe to your stream. \ No newline at end of file diff --git a/R5ProTestbed/Tests/PublishCustomSource/CustomVideoSource.swift b/R5ProTestbed/Tests/PublishCustomSource/CustomVideoSource.swift new file mode 100644 index 0000000..23f5a46 --- /dev/null +++ b/R5ProTestbed/Tests/PublishCustomSource/CustomVideoSource.swift @@ -0,0 +1,152 @@ +// +// CustomVideoSource.swift +// R5ProTestbed +// +// Created by David Heimann on 5/9/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(CustomVideoSource) +class CustomVideoSource : R5VideoSource { + + var frameDuration : CMTime; + var PTS : CMTime; + var timer : NSTimer?; + + override init() { + + let fpsVal: Int32 = 15; + + //setup simple timestamp calculation + self.frameDuration = CMTimeMakeWithSeconds(0.1, fpsVal); + self.PTS = kCMTimeZero; + self.timer = nil; + + super.init(); + + //initialize the properties used by the stream! + self.bitrate = 256; + self.width = 320; + self.height = 240; + self.orientation = 0; + self.fps = fpsVal; + } + + override func startVideoCapture() { + + //start a timer to run at desired framerate. + self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0 / Double(self.fps), + target: self, + selector: #selector(capturePixels), + userInfo: nil, + repeats: true); + } + + override func stopVideoCapture() { + + //stop the capture! + self.timer!.invalidate(); + } + + func capturePixels( time:NSTimer ){ + + //make sure encoding layer is ready for input! + if(self.encoder != nil){ + + let frameSize: CGSize = CGSizeMake(352, 288); + + // + // Below is a simple "plasma" style rendering using the PTS as the animation offset + // Using RGB color format - 3 bytes per pixel + // + + let time: Float = Float( CMTimeGetSeconds(self.PTS) * 0.6 ); + let scale: Float = 0.035; + let componentsPerPixel = 3; + let rgbPixels: UnsafeMutablePointer<__uint8_t> = UnsafeMutablePointer<__uint8_t>.alloc(Int(frameSize.width * frameSize.height) * componentsPerPixel); + + //stride + let bpr: Int = Int(frameSize.width) * componentsPerPixel; + + for y in 0.. +[CustomVideoSource.swift #24](CustomVideoSource.swift#24) + + +To handle the processing of custom data, you can overwrite `R5VideoSource.startVideoCapture`. This method is called by the stream when it is ready for video content. For this example, we will start a simple timer to call `capturePixels` at our desired framerate. + +```Swift +override func startVideoCapture() { + + //start a timer to run at desired framerate. + self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0 / Double(self.fps), + target: self, + selector: #selector(capturePixels), + userInfo: nil, + repeats: true); +} +``` + +[CustomVideoSource.swift #38](CustomVideoSource.swift#38) + + +Handle the stop of the rendering call also be handled in `R5VideoSource.stopVideoCapture`. + +```Swift +override func stopVideoCapture() { + + //stop the capture! + self.timer!.invalidate(); +} +``` + +[CustomVideoSource.swift #48](CustomVideoSource.swift#48) + + +The last thing to do is pass a `CVPixelBufferRef` to the encoder. `capturePixels:` is called by the timer at the interval specified. + +There is some additional math that won't be covered in this example, so it will be skipped over. + +The first thing that is needed is an array of bytes that will hold the pixel data. This example will be using RGB data, so there are 3 bytes per pixel. + +```Swift +func capturePixels( time:NSTimer ){ + +... + +let componentsPerPixel = 3; +let rgbPixels: UnsafeMutablePointer<__uint8_t> = UnsafeMutablePointer<__uint8_t>.alloc(Int(frameSize.width * frameSize.height) * componentsPerPixel); +``` + +[CustomVideoSource.swift #54](CustomVideoSource.swift#54) + + +Each pixel can then be assigned a color (between 0-255 in this case) for the RGB channels. + +```Swift +//Set the R, G, B channels to the desired color +rgbPixels[(y * bpr) + (componentsPerPixel*x)] = __uint8_t( max( Double(sinf( v * Float(M_PI) )) * Double(UINT8_MAX), 0) ); +rgbPixels[(y * bpr) + (componentsPerPixel*x)+1] = __uint8_t( max( Double(cosf( v * Float(M_PI) )) * Double(UINT8_MAX), 0) ); +rgbPixels[(y * bpr) + (componentsPerPixel*x)+2] = 0; +``` + +[CustomVideoSource.swift #89](CustomVideoSource.swift#89) + + +Once the bytes have been set to their desired RGB values, we create a `CVPixelBufferRef` that will hold that date for processing. + +```Swift +// Create a pixel buffer +var pixelBuffer: CVPixelBuffer?; + +//Create a pixel buffer to hold our bytes with RGB format +var result: OSStatus = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, + Int(frameSize.width), + Int(frameSize.height), + kCVPixelFormatType_24RGB, + rgbPixels, + Int(frameSize.width) * componentsPerPixel, + nil, + &pixelBuffer, + nil, + &pixelBuffer); +``` + +[CustomVideoSource.swift #96](CustomVideoSource.swift#96) + + +Next, we can create the buffer information that is needed. The video format information can be extracted from the pixel buffer, and the only timing information that is required is the timestamp. + +```Swift +var videoInfo: CMVideoFormatDescriptionRef?; + +//Create a description for the pixel buffer +result = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer!, &videoInfo); + +if(result != kCVReturnSuccess) { + NSLog("Failed to create video info"); +} + +//Only PTS is needed for the encoder - leave everything else invalid if you want +var timingInfo: CMSampleTimingInfo = kCMTimingInfoInvalid; +timingInfo.duration = kCMTimeInvalid; +timingInfo.decodeTimeStamp = kCMTimeInvalid; +timingInfo.presentationTimeStamp = self.PTS; +``` + +[CustomVideoSource.swift #115](CustomVideoSource.swift#115) + + +Next, we can create our `CMSampleBufferRef` using the `CVPixelBufferRef`, `videoInfo`, and `timingInfo`. + +```Swift +var buffer: CMSampleBufferRef?; + +//Create the sample buffer for the pixel buffer +result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, + pixelBuffer!, + true, nil, nil, + videoInfo!, + &timingInfo, + &buffer); +``` + +[CustomVideoSource.swift #130](CustomVideoSource.swift#130) + + +The last thing to do is pass the buffer to our encoder, which is part of the `R5VideoSource`, and to increment our timestamp and free up all memory. + +```Swift +//push the sample buffer to the encoder with type r5_media_type_video_custom +if(!self.pauseEncoding){ + self.encoder.encodeFrame( buffer, ofType: r5_media_type_video_custom ); +} + +//increment our timestamp +self.PTS = CMTimeAdd(self.PTS, self.frameDuration); + +//free all our content +free(rgbPixels); +``` + +[CustomVideoSource.swift #140](CustomVideoSource.swift#140) + + + +***It is very important to remember to use `r5_media_type_video_custom` to pass this buffer into the encoder. The standard video format expects a CVImageBufferRef and will fail to encode with custom pixel data!*** + + +This is all that is needed to pass in RGB pixel data in place of the built in camera. The last thing we have to do is pass this object to the `R5Stream` + +```Swift +// Attach the custom source to the stream +let videoSource: CustomVideoSource = CustomVideoSource(); +self.publishStream!.attachVideo(videoSource); + +``` + +[PublishCustomSourceTest.swift #31](PublishCustomSourceTest.swift#31) + \ No newline at end of file diff --git a/R5ProTestbed/Tests/PublishOrientation/PublishOrientationTest.swift b/R5ProTestbed/Tests/PublishOrientation/PublishOrientationTest.swift new file mode 100644 index 0000000..012a5bb --- /dev/null +++ b/R5ProTestbed/Tests/PublishOrientation/PublishOrientationTest.swift @@ -0,0 +1,58 @@ +// +// PublishOrientationTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/18/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(PublishOrientationTest) +class PublishOrientationTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + let cam = self.publishStream?.getVideoSource() as! R5Camera + + cam.orientation = cam.orientation + 90 + + self.publishStream?.updateStreamMeta() + + } + + + +} diff --git a/R5ProTestbed/Tests/PublishOrientation/README.md b/R5ProTestbed/Tests/PublishOrientation/README.md new file mode 100644 index 0000000..cde8122 --- /dev/null +++ b/R5ProTestbed/Tests/PublishOrientation/README.md @@ -0,0 +1,22 @@ +#Publish Orientation + +`R5Camera.orientation` allows the user to rotate the video source for a stream without interupting the broadcast. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[PublishOrientationTest.swift](PublishOrientationTest.swift)*** + +##Running the example +Touch the screen at any time while streaming to rotate the video source by 90 degrees. It's sugested that you verify this change with a separate device. + +##Using R5Camera.orientation +`R5Camera.orientation` will tell you how much the current video source is rotated from what how it's coming into the application. By getting the instance of R5Camera attached to the R5Stream object and changing its orientation property, this can be modified live for the stream. Once streaming, simply call: + +``` +Swift +let cam = self.publishStream?.getVideoSource() as! R5Camera +cam.orientation = cam.orientation + 90 +``` + +[PublishOrientationTest.swift #66](PublishOrientationTest.swift#L66) + \ No newline at end of file diff --git a/R5ProTestbed/Tests/PublishStreamImage/PublishStreamImageTest.swift b/R5ProTestbed/Tests/PublishStreamImage/PublishStreamImageTest.swift new file mode 100644 index 0000000..513e037 --- /dev/null +++ b/R5ProTestbed/Tests/PublishStreamImage/PublishStreamImageTest.swift @@ -0,0 +1,61 @@ +// +// PublishStreamImageTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/17/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(PublishStreamImageTest) +class PublishStreamImageTest: BaseTest { + + var uiv : UIImageView? = nil + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + + uiv = UIImageView(frame: CGRect(x: 0, y: self.view.frame.height-200, width: 300, height: 200)) + uiv!.contentMode = UIViewContentMode.ScaleAspectFit + self.view.addSubview(uiv!); + + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + + uiv!.image = self.publishStream?.getStreamImage(); + + } + + +} diff --git a/R5ProTestbed/Tests/PublishStreamImage/README.md b/R5ProTestbed/Tests/PublishStreamImage/README.md new file mode 100644 index 0000000..057d1de --- /dev/null +++ b/R5ProTestbed/Tests/PublishStreamImage/README.md @@ -0,0 +1,22 @@ +#Publish Image Capture + +`R5Stream.getStreamImage` allows the user to capture a screenshot of the stream at any time. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[PublishStreamImageTest.swift](PublishStreamImageTest.swift)*** + +##Running the example +Touch the screen at any time while streaming to popup a temporary overlay containing the UIImage that is returned from the Red5 Pro SDK. + +##Using getStreamImage +`R5Stream.getStreamImage` returns a UIImage containing a screenshot of the current stream. The image dimensions match the incoming stream dimensions, and contain RGB data. Once streaming, simply call: + +```Swift +uiv!.image = self.publishStream?.getStreamImage(); +``` + +[PublishStreamImageTest.swift #56](PublishStreamImageTest.swift#L56) + + +The UIImage can be saved to disk, displayed with a UIImageView, or processed in any way that is needed. \ No newline at end of file diff --git a/R5ProTestbed/Tests/PublishStreamManager/PublishStreamManagerTest.swift b/R5ProTestbed/Tests/PublishStreamManager/PublishStreamManagerTest.swift new file mode 100644 index 0000000..8402891 --- /dev/null +++ b/R5ProTestbed/Tests/PublishStreamManager/PublishStreamManagerTest.swift @@ -0,0 +1,87 @@ +// +// PublishStreamManagerTest.swift +// R5ProTestbed +// +// Created by David Heimann on 4/8/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(PublishStreamManagerTest) +class PublishStreamManagerTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in }; + + setupDefaultR5VideoViewController() + + // url format https://{streammanagerhost}:{port}/streammanager/api/1.0/event/{scopeName}/{streamName}?action=broadcast + let urlString = "http://" + (Testbed.getParameter("host") as! String) + ":5080/streammanager/api/1.0/event/" + + (Testbed.getParameter("context") as! String) + "/" + + (Testbed.getParameter("stream1") as! String) + "?action=broadcast" + + + NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(string: urlString)! ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in + + if ((error) != nil) { + NSLog("%@", error!); + return; + } + + // Convert our response to a usable NSString + let dataAsString = NSString( data: data!, encoding: NSUTF8StringEncoding) + + // The string above is in JSON format, we specifically need the serverAddress value + var json: [String: AnyObject] + do{ + json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String: AnyObject] + }catch{ + print(error) + return + } + + let ip = json["serverAddress"] as! String + + NSLog("Retrieved %@ from %@, of which the usable IP is %@", dataAsString!, urlString, ip); + + // Setup a configuration object for our connection + let config = R5Configuration() + config.host = ip + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + + // Create a new connection using the configuration above + let connection = R5Connection(config: config) + + // UI updates must be asynchronous + dispatch_async(dispatch_get_main_queue(), { + // Create our new stream that will utilize that connection + self.setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(self.publishStream!) + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + let label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height-24, width: self.view.frame.width, height: 24)) + label.textAlignment = NSTextAlignment.Left + label.backgroundColor = UIColor.lightGrayColor() + label.text = "Connected to: " + ip + self.view.addSubview(label) + }) + } + ) + + } + +} diff --git a/R5ProTestbed/Tests/PublishStreamManager/README.md b/R5ProTestbed/Tests/PublishStreamManager/README.md new file mode 100644 index 0000000..ca9b01c --- /dev/null +++ b/R5ProTestbed/Tests/PublishStreamManager/README.md @@ -0,0 +1,40 @@ +#Stream Manager Publishing + +With clustering, we need to determine which red5 pro instance the client will use. The other examples used a static configuration ip for streaming endpoints. Basic clustering uses more than one stream endpoint for subscribers. Advanced clustering uses more than one endpoint for publishers also. With the Stream Manager, our configuration ip will be used similarly for publishers and subscribers. Both publishers and subscribers will call a web service to receive the ip that should be used. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[PublishStreamManagerTest.swift](PublishStreamManagerTest.swift)*** + +###Setup +In order to publish, you first need to connect to the origin server's Stream Manager. The Stream Manager will know which edges are active and provide the one that needs to be published to. + +```Swift +let urlString = "https://" + (Testbed.getParameter("host") as! String) + ":5080/streammanager/api/1.0/event/" + + Testbed.getParameter("context") as! String + "/" + + Testbed.getParameter("stream1") as! String + "?action=broadcast" + +NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(string: urlString)! ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in +``` + +[PublishStreamManagerTest.swift #24](PublishStreamManagerTest.swift#L24) + + +The service returns a json object with the information needed to connect to publish. + +```Swift +do{ + let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) +}catch{ + print(error) + return +} + +if let ip = ["serverAddress"] as? [String: AnyObject] { +``` + +[PublishStreamManagerTest.swift #43](PublishStreamManagerTest.swift#L43) + diff --git a/R5ProTestbed/Tests/Recorded/Readme.md b/R5ProTestbed/Tests/Recorded/Readme.md new file mode 100644 index 0000000..e59346c --- /dev/null +++ b/R5ProTestbed/Tests/Recorded/Readme.md @@ -0,0 +1,17 @@ +#Published Stream Recording + +`R5RecordTypeRecord` signals for the server to record the stream. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[RecordedTest.swift](RecordedTest.swift)*** + +##Recording +The only difference between this example and the publish test is that in the publish command you send a different RecordType flag: + +```Swift +self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeRecord) +``` + +[RecordedTest.swift #38](RecordedTest.swift#L38) + diff --git a/R5ProTestbed/Tests/Recorded/RecordedTest.swift b/R5ProTestbed/Tests/Recorded/RecordedTest.swift new file mode 100644 index 0000000..a842638 --- /dev/null +++ b/R5ProTestbed/Tests/Recorded/RecordedTest.swift @@ -0,0 +1,43 @@ +// +// RecordedTest.swift +// R5ProTestbed +// +// Created by David Heimann on 3/15/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(RecordedTest) +class RecordedTest: BaseTest { + + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeRecord) + + + + } +} diff --git a/R5ProTestbed/Tests/RemoteCall/PublishRemoteCallTest.swift b/R5ProTestbed/Tests/RemoteCall/PublishRemoteCallTest.swift new file mode 100644 index 0000000..5e34f16 --- /dev/null +++ b/R5ProTestbed/Tests/RemoteCall/PublishRemoteCallTest.swift @@ -0,0 +1,64 @@ +// +// PublishRemoteCallTest.swift +// R5ProTestbed +// +// Created by David Heimann on 4/26/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(PublishRemoteCallTest) +class PublishRemoteCallTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in + + }; + + setupDefaultR5VideoViewController() + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + self.currentView!.attachStream(publishStream!) + + + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + + } + + override func onR5StreamStatus(stream: R5Stream!, withStatus statusCode: Int32, withMessage msg: String!) { + + if(Int(statusCode) == Int(r5_status_start_streaming.rawValue)){ + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + } + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + var sendString : String = ""; + let touchLoc = recognizer.locationOfTouch(0, inView: self.view) + let size = self.view.bounds.size + + sendString += "message=The publisher wants your attention;" + sendString += "touchX=" + (touchLoc.x/size.width).description + ";" + sendString += "touchY=" + (touchLoc.y/size.height).description + + publishStream?.send("whateverFunctionName", withParam: sendString) + } + + +} diff --git a/R5ProTestbed/Tests/RemoteCall/README.md b/R5ProTestbed/Tests/RemoteCall/README.md new file mode 100644 index 0000000..901852c --- /dev/null +++ b/R5ProTestbed/Tests/RemoteCall/README.md @@ -0,0 +1,81 @@ +#Remote Procedure Calls + +`R5Stream.send` allows the publisher to send messages to the server to be sent to all subscribers. + + +###Example Code +- ***[PublishRemoteCallTest.swift](PublishRemoteCallTest.swift)*** +- ***[SubscribeRemoteCallTest.swift](SubscribeRemoteCallTest.swift)*** + +- ***[BaseTest.swift](../BaseTest.swift)*** + +##Running the example +Two devices are required to run this example. One as a publisher, and the other as a subscriber. + +Connect the first device (publisher) with the Publish - Remote Call example. On the second device (subscriber) use the Subscribe - Remote Call example. + +Touch the preview on the publisher screen to display a label on the subscriber screen where the publisher touched. + + +##Using the R5Stream send +Once the stream has connected you are able to dispatch messages to any connected subscribers. Sending the message is a simple call: + +```Swift +publishStream?.send("whateverFunctionName", withParam: sendString) +``` + +[PublishRemoteCallTest.swift #60](PublishRemoteCallTest.swift#L60) + + +###Send Message Format +The publisher send has a specific parameter format that must be observed. A single string variable is able to be sent, and contains a map of all key-value pairs sepereated by a semi-colon. + +```Swift +"key1=value1;key2=value2;key3=value3;" +``` +Not using this format can result in parsing failure on the server and messages will not be dispatched. + +##Receiving R5Stream send calls +In order to handle `R5Stream.send` calls from the publisher, the `R5Stream.client` delegate must be set. This delegate will receive all `R5Stream.send` messages via appropriately named methods. + +```Swift +self.subscribeStream?.client = self; +``` + +[SubscribeRemoteCallTest.swift #27](SubscribeRemoteCallTest.swift#L27) + + +Because the publisher will be sending **whateverFunctionName**, the subscriber client delegate will need a matching method signature. As the name implies, the function can be named anything as long as it is publicly accessible. All methods receive a single string argument containing the variable map provided by the publisher. This map can easily be parsed. + +```Swift +func whateverFunctionName(message: String){ + + NSLog("Got this message: " + message) + + let splitMessage = message.characters.split(";").map(String.init) + + var message : String = "" + var point : CGPoint = CGPoint() + + for item in splitMessage { + + let itemSplit = item.characters.split("=").map(String.init) + let size = self.view.frame.size + switch itemSplit[0] { + case "message": + message = itemSplit[1] + break + case "touchX": + point.x = CGFloat((itemSplit[1] as NSString).doubleValue) * size.width + break + case "touchY": + point.y = CGFloat((itemSplit[1] as NSString).doubleValue) * size.height + break + default: + break + } + } +``` + +[SubscribeRemoteCallTest.swift #36](SubscribeRemoteCallTest.swift#L36) + diff --git a/R5ProTestbed/Tests/RemoteCall/SubscribeRemoteCallTest.swift b/R5ProTestbed/Tests/RemoteCall/SubscribeRemoteCallTest.swift new file mode 100644 index 0000000..fe539c8 --- /dev/null +++ b/R5ProTestbed/Tests/RemoteCall/SubscribeRemoteCallTest.swift @@ -0,0 +1,82 @@ +// +// SubscribeRemoteCallTest.swift +// R5ProTestbed +// +// Created by David Heimann on 4/27/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeRemoteCallTest) +class SubscribeRemoteCallTest: BaseTest { + var label : UILabel? = nil + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + + setupDefaultR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + currentView?.attachStream(subscribeStream) + + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + } + + func whateverFunctionName(message: String){ + + NSLog("Got this message: " + message) + + let splitMessage = message.characters.split(";").map(String.init) + + var message : String = "" + var point : CGPoint = CGPoint() + + for item in splitMessage { + + let itemSplit = item.characters.split("=").map(String.init) + let size = self.view.frame.size + switch itemSplit[0] { + case "message": + message = itemSplit[1] + break + case "touchX": + point.x = CGFloat((itemSplit[1] as NSString).doubleValue) * size.width + break + case "touchY": + point.y = CGFloat((itemSplit[1] as NSString).doubleValue) * size.height + break + default: + break + } + } + + dispatch_async(dispatch_get_main_queue(), { + + if( self.label == nil){ + self.label = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 24)) + self.label!.textAlignment = NSTextAlignment.Left + self.label!.backgroundColor = UIColor.lightGrayColor() + self.view.addSubview(self.label!) + } + + self.label!.text = message + + var frame = self.label!.frame + frame.origin = point + self.label!.frame = frame + self.label!.sizeToFit() + }) + + } +} diff --git a/R5ProTestbed/Tests/Subscribe/README.md b/R5ProTestbed/Tests/Subscribe/README.md new file mode 100644 index 0000000..27b6d87 --- /dev/null +++ b/R5ProTestbed/Tests/Subscribe/README.md @@ -0,0 +1,106 @@ +#Subscribing on Red5 Pro + +This example shows how to easily subscribe to a Red5 Pro stream. + +###Example Code + +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[SubscribeTest.swift](SubscribeTest.swift)*** + +##How to Subscribe +Subscribing to a Red5 Pro stream requires a few components to function fully. +####Setup R5Connection +The R5Connection manages the connection that the stream utilizes. You will need to setup a configuration and intialize a new connection. + +``` +Swift +func getConfig()->R5Configuration{ + // Set up the configuration + let config = R5Configuration() + config.host = Testbed.getParameter("host") as! String + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + return config +} +``` + +[BaseTest.swift #50](../BaseTest.swift#L50) + + +``` +Swift +let config = getConfig() +// Set up the connection and stream +let connection = R5Connection(config: config) +``` + +[SubscribeTest.swift #27](SubscribeTest.swift#L27) + + +####Setup R5Stream +The `R5Stream` handles both subscribing and publishing. Creating one simply requires the connection already created. + +``` +Swift +//Create our new stream that will utilize that connection +self.publishStream = R5Stream(connection: connection) +//Setup our listener to handle events from this stream +self.publishStream!.delegate = self +``` + +[BaseTest.swift #75](../BaseTest.swift#L75) + + +The `R5StreamDelegate` that is assigned to the `R5Stream` will receive status events for that stream, including connecting, disconnecting, and errors. + + +#### Preview the Subscriber +The `R5VideoViewController` will present publishing streams as well as subscribed streams. To view the subscribing stream, it simply needs to attach the `R5Stream`. + +A `R5VideoViewController` can be set on any UIViewController, or created programmatically + +``` +Swift +let r5View : R5VideoViewController = getNewR5VideoViewController(self.view.frame); +self.addChildViewController(r5View); +``` + +[BaseTest.swift #121](../BaseTest.swift#L121) + + +To view the preview before publishing has started, use `R5VideoViewController.showPreview`. + +``` +Swift +view.addSubview(r5View.view) + +r5View.showPreview(true) + +r5View.showDebugInfo(true) +``` + +[BaseTest.swift #121](../BaseTest.swift#L121) + + +Lastly, we attach the Stream to the R5VideoView to see the streaming content. + +``` +Swift +currentView?.attachStream(subscribeStream) +``` + +[SubscribeTest.swift #34](SubscribeTest.swift#L34) + + +####Start Subscribing +The `R5Stream.Subscribe` method will establish the server connection and begin Subscribing. + +``` +Swift +self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) +``` + +[SubscribeTest.swift #37](SubscribeTest.swift#L37) + diff --git a/R5ProTestbed/Tests/Subscribe/SubscribeTest.swift b/R5ProTestbed/Tests/Subscribe/SubscribeTest.swift new file mode 100644 index 0000000..57f3bd7 --- /dev/null +++ b/R5ProTestbed/Tests/Subscribe/SubscribeTest.swift @@ -0,0 +1,50 @@ +// +// SubscribeTestViewController.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeTest) +class SubscribeTest: BaseTest { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + + setupDefaultR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + currentView?.attachStream(subscribeStream) + + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + + } + + + func onMetaData(data : String){ + + } + + + +} diff --git a/R5ProTestbed/Tests/SubscribeAspectRatio/README.md b/R5ProTestbed/Tests/SubscribeAspectRatio/README.md new file mode 100644 index 0000000..b57bf1c --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeAspectRatio/README.md @@ -0,0 +1,37 @@ +#Subscriber Aspect Ratio + +`R5VideoViewController.scaleMode` controls the display mode of the content that is being pushed to it. Depending on the value the content will scale to the apropriate fill value. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[SubscribeAspectRatioTest.swift](SubscribeAspectRatioTest.swift)*** + +##Running the example +Begin by publishing to **stream1** from a second device. **stream1** is the default stream1 name that is used by this example. + +Touch the screen at any time while streaming to change the scale mode, affecting how the stream is display on the user's end. + +##Using scaleMode +R5VideoViewController.scaleMode has 3 potential enum values. + +``` +r5_scale_to_fill: scale to fill and maintain aspect ratio (cropping will occur) +r5_scale_to_fit: scale to fit inside view (letterboxing will occur) +r5_scale_fill: scale to fill view (will not respect aspect ratio of video) +``` + +By default, this value is `r5_scale_to_fill` This example handles scalemode by raw value in order to cycle through its values when it receives a tap. + +``` +Swift +var nextMode = (currentView?.scaleMode.rawValue)! + 1; +//A value of 3 or larger won't parse correctly to the enum, so it's reset to 0 +if(nextMode == 3){ + nextMode = 0; +} + +currentView?.scaleMode = r5_scale_mode(nextMode); +``` + +[SubscribeAspectRatioTest.swift #39](SubscribeAspectRatioTest.swift#L39) + \ No newline at end of file diff --git a/R5ProTestbed/Tests/SubscribeAspectRatio/SubscribeAspectRatioTest.swift b/R5ProTestbed/Tests/SubscribeAspectRatio/SubscribeAspectRatioTest.swift new file mode 100644 index 0000000..5737a33 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeAspectRatio/SubscribeAspectRatioTest.swift @@ -0,0 +1,48 @@ +// +// SubscribeAspectRatioTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/18/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeAspectRatioTest) +class SubscribeAspectRatioTest: BaseTest { + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + + setupDefaultR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + + currentView?.attachStream(subscribeStream) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + var nextMode = (currentView?.scaleMode.rawValue)! + 1; + if(nextMode == 3){ + nextMode = 0; + } + + currentView?.scaleMode = r5_scale_mode(nextMode); + + } + +} diff --git a/R5ProTestbed/Tests/SubscribeCluster/README.md b/R5ProTestbed/Tests/SubscribeCluster/README.md new file mode 100644 index 0000000..1797558 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeCluster/README.md @@ -0,0 +1,34 @@ +#Subscribing To a Cluster Server + +#Publishing and subscribing with Red5 Pro clusters With clustering, we need to determine which red5 pro instance the client will use. The other examples used a static configuration ip for streaming endpoints. Basic clustering uses more than one stream endpoint for subscribers. Advanced clustering uses more than one endpoint for publishers also. In the basic clustering scenario, our configuration ip will be used differently for publishers and subscribers. Publishers will stream directly to the configuration ip. Subscribers will not. Instead, subscribers will call a web service to receive the ip that should be used. + +###Example Code + +- ***[SubscribeCluster.swift](SubscribeCluster.swift)*** + +##Configuration of the server. The cluster.xml file located in the conf directory. If the server is an edge, add an ip for its origin(s) within the origins list. Every server in your cluster must use the same password or core connections are denied. Set the public facing ip and port. Origins provide round robin, and you can exclude instances from it by setting the privateInstance property to true on the edge. The hidden edge can be used for other purposes such as being a repeater origin. ``` 0.0.0.0:1935 ``` The round robin servlet is defined in web.xml of your webapp. It is the service point subscribers use to get a playback-ip. ``` cluster com.red5pro.cluster.plugin.agent.ClusterWebService 2 cluster /cluster ``` The uri would be http://YOUR_IP:5080/YOURAPP/cluster ##How to Publish Publishers must use the origin ip in their configuration. The stream is distributed to the edges from the origin. Use the publish example or a flash broadcaster to provide a live stream. + ##How to Subscribe Subscribers call the cluster servlet, and use the return data as the configuration ip. The origin will know which edges are active and provide the next in sequence. + +```Swift +let urlString = "http://" + (Testbed.getParameter("host") as! String) + ":5080/cluster" + +NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(fileURLWithPath: urlString) ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in + + if ((error) != nil) { + NSLog("%@", error!); + return; + } + + // Convert our response to a usable NSString + let dataAsString = NSString( data: data!, encoding: NSUTF8StringEncoding) + + // The string above is formatted like 99.98.97.96:1234, but we won't need the port portion + let ip = dataAsString?.substringToIndex((dataAsString?.rangeOfString(":").location)!) + NSLog("Retrieved %@ from %@, of which the usable IP is %@", dataAsString!, urlString, ip!); +``` + +[SubscribeCluster.swift #19](SubscribeCluster.swift#L19) + diff --git a/R5ProTestbed/Tests/SubscribeCluster/SubscribeCluster.swift b/R5ProTestbed/Tests/SubscribeCluster/SubscribeCluster.swift new file mode 100644 index 0000000..4c6f213 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeCluster/SubscribeCluster.swift @@ -0,0 +1,77 @@ +// +// SubscribeCluster.swift +// R5ProTestbed +// +// Created by David Heimann on 3/21/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeCluster) +class SubscribeCluster: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + let urlString = "http://" + (Testbed.getParameter("host") as! String) + ":5080/cluster" + + NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(string: urlString)! ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in + + if ((error) != nil) { + NSLog("%@", error!); + return; + } + + // Convert our response to a usable NSString + let dataAsString = NSString( data: data!, encoding: NSUTF8StringEncoding) + + // The string above is formatted like 99.98.97.96:1234, but we won't need the port portion + let ip = dataAsString?.substringToIndex((dataAsString?.rangeOfString(":").location)!) + NSLog("Retrieved %@ from %@, of which the usable IP is %@", dataAsString!, urlString, ip!); + + // Setup a configuration object for our connection + let config = R5Configuration() + config.host = ip + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + + // Create a new connection using the configuration above + let connection = R5Connection(config: config) + + // UI updates must be asynchronous + dispatch_async(dispatch_get_main_queue(), { + // Create our new stream that will utilize that connection + self.subscribeStream = R5Stream(connection: connection) + + // Setup our listener to handle events from this stream + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self + + // Setup our R5VideoViewController to display the stream content + self.setupDefaultR5VideoViewController() + + // Attach the R5VideoViewController to our publishing stream + self.currentView?.attachStream(self.subscribeStream) + + // Start subscribing!! + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + let label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height-24, width: self.view.frame.width, height: 24)) + label.textAlignment = NSTextAlignment.Left + label.backgroundColor = UIColor.lightGrayColor() + label.text = "Connected to: " + ip! + self.view.addSubview(label) + }) + }) + + } + +} diff --git a/R5ProTestbed/Tests/SubscribeNoView/README.md b/R5ProTestbed/Tests/SubscribeNoView/README.md new file mode 100644 index 0000000..e4a42a2 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeNoView/README.md @@ -0,0 +1,12 @@ +#Subscriber With No View + +Streams don't need to be connected to a view in order to work. While that will prevent them from displaying video, if a stream is audio-only, it will still play completely. The example will look very similar to the basic subscribe example, with the notable exception that a display doesn't need to be created or attached. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[SubscribeNoViewTest.swift](SubscribeNoViewTest.swift)*** + +##Running the example +Begin by publishing to **stream1** from a second device. This stream should be audio only, but if it has video, that won't prevent the audio from playing. + +Select the example and it should begin playing any audio that is picked up by the other device. diff --git a/R5ProTestbed/Tests/SubscribeNoView/SubscribeNoViewTest.swift b/R5ProTestbed/Tests/SubscribeNoView/SubscribeNoViewTest.swift new file mode 100644 index 0000000..843e0d0 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeNoView/SubscribeNoViewTest.swift @@ -0,0 +1,26 @@ +// +// SubscribeNoViewTest.swift +// R5ProTestbed +// +// Created by David Heimann on 5/6/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeNoViewTest) +class SubscribeNoViewTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + } + +} diff --git a/R5ProTestbed/Tests/SubscribeReconnect/SubscribeAutoReconnectTest.swift b/R5ProTestbed/Tests/SubscribeReconnect/SubscribeAutoReconnectTest.swift new file mode 100644 index 0000000..68699cd --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeReconnect/SubscribeAutoReconnectTest.swift @@ -0,0 +1,75 @@ +// +// SubscribeTestViewController.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/16/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeAutoReconnectTest) +class SubscribeAutoReconnectTest: BaseTest { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + + setupDefaultR5VideoViewController() + + self.Subscribe() + + } + + func Subscribe(){ + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + currentView?.attachStream(subscribeStream) + + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + + } + + override func onR5StreamStatus(stream: R5Stream!, withStatus statusCode: Int32, withMessage msg: String!) { + + super.onR5StreamStatus(stream, withStatus: statusCode, withMessage: msg) + + + + if(statusCode == Int32(r5_status_connection_error.rawValue)){ + + //we can assume it failed here! + + NSLog("Connection error") + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in + self.Subscribe() + } + } + + } + + + func onMetaData(data : String){ + + } + + + +} diff --git a/R5ProTestbed/Tests/SubscribeSetSize/SubscribeSetSizeTest.swift b/R5ProTestbed/Tests/SubscribeSetSize/SubscribeSetSizeTest.swift new file mode 100644 index 0000000..5f91b86 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeSetSize/SubscribeSetSizeTest.swift @@ -0,0 +1,70 @@ +// +// SubscribeAspectRatioTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/18/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeSetSizeTest) +class SubscribeSetSizeTest: BaseTest { + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + setupR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + + currentView?.attachStream(subscribeStream) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + } + + func setupR5VideoViewController() -> R5VideoViewController{ + + let r5View : R5VideoViewController = getNewR5VideoViewController(self.view.frame); + self.addChildViewController(r5View); + + view.addSubview(r5View.view) + + r5View.showPreview(true) + + r5View.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + currentView = r5View; + + r5View.setFrame(CGRect(x: 100, y: 100, width: 200, height: 200)); + + return currentView! + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + currentView!.setFrame(CGRect(x: 100, y: 100, width: 200, height: 200)); + + } + + override func viewDidLayoutSubviews() { + + + if(currentView != nil){ + + // currentView?.setFrame(view.frame); + } + + } + +} diff --git a/R5ProTestbed/Tests/SubscribeStreamImage/README.md b/R5ProTestbed/Tests/SubscribeStreamImage/README.md new file mode 100644 index 0000000..a2c9be5 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeStreamImage/README.md @@ -0,0 +1,25 @@ +#Subcriber Image Capture + +`R5Stream.getStreamImage` allows the user to capture a screenshot of the stream at any time. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[SubscribeStreamImageTest.swift](SubscribeStreamImageTest.swift)*** + +##Running the example +Begin by publishing to **stream1** from a second device. **stream1** is the default stream1 name that is used by this example. Select the **SubscribeStreamImageTest** option to open a subscriber view. + +Touch the screen at any time while streaming to popup a temporary overlay containing the UIImage that is returned from the Red5 Pro SDK. + +##Using getStreamImage +`R5Stream.getStreamImage` returns a UIImage containing a screenshot of the current stream. The image dimensions match the incoming stream dimensions, and contain RGB data. Once streaming, simply call: + +``` +Swift +uiv!.image = self.publishStream?.getStreamImage(); +``` + +[SubscribeStreamImageTest.swift #56](SubscribeStreamImageTest.swift#L56) + + +The UIImage can be saved to disk, displayed with a UIImageView, or processed in any way that is needed. \ No newline at end of file diff --git a/R5ProTestbed/Tests/SubscribeStreamImage/SubscribeStreamImageTest.swift b/R5ProTestbed/Tests/SubscribeStreamImage/SubscribeStreamImageTest.swift new file mode 100644 index 0000000..d4ff6d8 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeStreamImage/SubscribeStreamImageTest.swift @@ -0,0 +1,55 @@ +// +// PublishStreamImageTest.swift +// R5ProTestbed +// +// Created by Andy Zupko on 12/17/15. +// Copyright © 2015 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeStreamImageTest) +class SubscribeStreamImageTest: BaseTest { + + var uiv : UIImageView? = nil + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + + setupDefaultR5VideoViewController() + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + currentView?.attachStream(subscribeStream) + + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + + self.view.addGestureRecognizer(tap) + + + uiv = UIImageView(frame: CGRect(x: 0, y: self.view.frame.height-200, width: 300, height: 200)) + uiv!.contentMode = UIViewContentMode.ScaleAspectFit + self.view.addSubview(uiv!); + + } + + func handleSingleTap(recognizer : UITapGestureRecognizer) { + + + uiv!.image = self.subscribeStream?.getStreamImage(); + + } + + +} diff --git a/R5ProTestbed/Tests/SubscribeStreamManager/README.md b/R5ProTestbed/Tests/SubscribeStreamManager/README.md new file mode 100644 index 0000000..9675c57 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeStreamManager/README.md @@ -0,0 +1,40 @@ +#Stream Manager Subscribing + +With clustering, we need to determine which red5 pro instance the client will use. The other examples used a static configuration ip for streaming endpoints. Basic clustering uses more than one stream endpoint for subscribers. Advanced clustering uses more than one endpoint for publishers also. With the Stream Manager, our configuration ip will be used similarly for publishers and subscribers. Both publishers and subscribers will call a web service to receive the ip that should be used. + +###Example Code +- ***[BaseTest.swift](../BaseTest.swift)*** +- ***[SubscribeStreamManagerTest.swft](SubscribeStreamManagerTest.swft)*** + +###Setup +In order to subscribe, you first need to connect to the origin server's Stream Manager. The Stream Manager will know which edges are active and provide the one that you need to subscribe from. + +```Swift +let urlString = "https://" + (Testbed.getParameter("host") as! String) + ":5080/streammanager/api/1.0/event/" + + Testbed.getParameter("context") as! String + "/" + + Testbed.getParameter("stream1") as! String + "?action=subscribe" + +NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(string: urlString)! ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in +``` + +[SubscribeStreamManagerTest.java #24](SubscribeStreamManagerTest.java#L24) + + +The service returns a json object with the information needed to connect to subscribe. + +```Swift +do{ + let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) +}catch{ + print(error) + return +} + +let ip = ["serverAddress"] +``` + +[SubscribeStreamManagerTest.swft #48](SubscribeStreamManagerTest.swft#L48) + diff --git a/R5ProTestbed/Tests/SubscribeStreamManager/SubscribeStreamManagerTest.swift b/R5ProTestbed/Tests/SubscribeStreamManager/SubscribeStreamManagerTest.swift new file mode 100644 index 0000000..702acc4 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeStreamManager/SubscribeStreamManagerTest.swift @@ -0,0 +1,93 @@ +// +// SubscribeStreamManagerTest.swift +// R5ProTestbed +// +// Created by David Heimann on 4/11/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeStreamManagerTest) +class SubscribeStreamManagerTest: BaseTest { + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in }; + + setupDefaultR5VideoViewController() + + // url format https://{streammanagerhost}:{port}/streammanager/api/1.0/event/{scopeName}/{streamName}?action=subscribe + let urlString = "http://" + (Testbed.getParameter("host") as! String) + ":5080/streammanager/api/1.0/event/" + + (Testbed.getParameter("context") as! String) + "/" + + (Testbed.getParameter("stream1") as! String) + "?action=subscribe" + + + NSURLConnection.sendAsynchronousRequest( + NSURLRequest( URL: NSURL(string: urlString)! ), + queue: NSOperationQueue(), + completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in + + if ((error) != nil) { + NSLog("%@", error!); + return; + } + + // Convert our response to a usable NSString + let dataAsString = NSString( data: data!, encoding: NSUTF8StringEncoding) + + // The string above is in JSON format, we specifically need the serverAddress value + var json: [String: AnyObject] + do{ + json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String: AnyObject] + }catch{ + print(error) + return + } + + if( json["serverAddress"] == nil ){ + NSLog("No server address returned"); + return; + } + + let ip = json["serverAddress"] as! String + + NSLog("Retrieved %@ from %@, of which the usable IP is %@", dataAsString!, urlString, ip); + + // Setup a configuration object for our connection + let config = R5Configuration() + config.host = ip + config.port = Int32(Testbed.getParameter("port") as! Int) + config.contextName = Testbed.getParameter("context") as! String + config.`protocol` = 1; + config.buffer_time = Testbed.getParameter("buffer_time") as! Float + + // Create a new connection using the configuration above + let connection = R5Connection(config: config) + + // UI updates must be asynchronous + dispatch_async(dispatch_get_main_queue(), { + // Create our new stream that will utilize that connection + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + self.currentView?.attachStream(self.subscribeStream) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + let label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height-24, width: self.view.frame.width, height: 24)) + label.textAlignment = NSTextAlignment.Left + label.backgroundColor = UIColor.lightGrayColor() + label.text = "Connected to: " + ip + self.view.addSubview(label) + }) + } + ) + + } + +} diff --git a/R5ProTestbed/Tests/SubscribeTwoStreams/Readme.md b/R5ProTestbed/Tests/SubscribeTwoStreams/Readme.md new file mode 100644 index 0000000..db5fcc8 --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeTwoStreams/Readme.md @@ -0,0 +1,31 @@ +#Two Way Video Chat + +This example demonstrates Subscribing to two different sources at once. + +###Example Code +- ***[SubscribeTwoStreams.swift](SubscribeTwoStreams.swift)*** + +- ***[BaseTest.swift](../BaseTest.swift)*** + +###Setup +This is intended to be used with two others using the two-way example to put on a presentation, allowing anyone using this client to watch them converse. + +###Managing Streams + +The special note to be aware of when it comes to handling two streams is that they each need to use a different audio controller. At the moment, the only ones available are ```R5AudioControllerModeStandardIO``` and ```R5AudioControllerModeEchoCancellation``` + +```Swift +self.subscribeStream!.audioController = R5AudioController(mode: R5AudioControllerModeStandardIO) +``` + +[SubscribeTwoStreams.swift #45](SubscribeTwoStreams.swift#L45) + + +```Swift +self.subscribeStream2?.audioController = R5AudioController(mode: R5AudioControllerModeEchoCancellation) +``` + +[SubscribeTwoStreams.swift #65](SubscribeTwoStreams.swift#L65) + + +This means that at this time, iOS can only subscribe to a maximum of two streams, but the process of subscribing to multiple streams is otherwise straightforward. \ No newline at end of file diff --git a/R5ProTestbed/Tests/SubscribeTwoStreams/SubscribeTwoStreams.swift b/R5ProTestbed/Tests/SubscribeTwoStreams/SubscribeTwoStreams.swift new file mode 100644 index 0000000..d216ceb --- /dev/null +++ b/R5ProTestbed/Tests/SubscribeTwoStreams/SubscribeTwoStreams.swift @@ -0,0 +1,81 @@ +// +// SubscribeTwoStreams.swift +// R5ProTestbed +// +// Created by David Heimann on 3/14/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(SubscribeTwoStreams) +class SubscribeTwoStreams: BaseTest { + var firstView : R5VideoViewController? = nil + var secondView : R5VideoViewController? = nil + var subscribeStream2 : R5Stream? = nil + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + + let screenSize = self.view.bounds.size + + firstView = getNewR5VideoViewController(CGRect( x: 0, y: 0, width: screenSize.width, height: screenSize.height / 2 )) + self.addChildViewController(firstView!) + view.addSubview((firstView?.view)!) + firstView?.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + firstView?.view.center = CGPoint( x: screenSize.width/2, y: screenSize.height/4 ) + + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + firstView?.attachStream(subscribeStream) + + self.subscribeStream!.audioController = R5AudioController(mode: R5AudioControllerModeStandardIO) + + self.subscribeStream!.play(Testbed.getParameter("stream1") as! String) + + + let connection2 = R5Connection(config: config) + + self.subscribeStream2 = R5Stream(connection: connection2 ) + self.subscribeStream2!.delegate = self + self.subscribeStream2?.client = self; + + secondView = getNewR5VideoViewController(CGRect( x: 0, y: screenSize.height / 2, width: screenSize.width, height: screenSize.height / 2 )) + self.addChildViewController(secondView!) + view.addSubview((secondView?.view)!) + secondView?.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + secondView?.view.center = CGPoint( x: screenSize.width/2, y: 3 * (screenSize.height/4) ) + + secondView?.attachStream(subscribeStream2) + + self.subscribeStream2?.audioController = R5AudioController(mode: R5AudioControllerModeEchoCancellation) + + self.subscribeStream2?.play(Testbed.getParameter("stream2") as! String) + } + + func onMetaData(data : String){ + + } + + override func closeTest() { + super.closeTest() + + if( self.subscribeStream2 != nil ){ + self.subscribeStream2!.stop() + } + } +} diff --git a/R5ProTestbed/Tests/TwoWay/Readme.md b/R5ProTestbed/Tests/TwoWay/Readme.md new file mode 100644 index 0000000..335186a --- /dev/null +++ b/R5ProTestbed/Tests/TwoWay/Readme.md @@ -0,0 +1,58 @@ +#Two Way Video Chat + +This example demonstrates two way communication using Red5 Pro. It also demonstrates using Remote Procedure Calls (RPC) on the server. + +###Example Code +- ***[TwoWayTest.swift](TwoWayTest.swift)*** + +- ***[BaseTest.swift](../BaseTest.swift)*** + +###Setup +Two way communication simply requires setting up a publish stream and a subscribe stream at the same time. You can test the example with two devices. On the second device use the "Home" screen to swap the names of the stream. + +The subscriber portion will automatically connect when the second person begins streaming. + +###Getting Live Streams +You can make RPC calls to the server using `R5Connection.call`. The call is similar to `R5Stream.send` but allows you to specify a return method name. + +`streams.getLiveStreams` is a built in RPC in all Red5 Pro servers. This call will return a string value that contains a json array of all streams that are currently publishing. + +```Swift + //call out to get our new stream + publishStream?.connection.call("streams.getLiveStreams", withReturn: "onGetLiveStreams", withParam: nil) +``` + +[TwoWayTest.swift #83](TwoWayTest.swift#L83) + + +The return method will be called on the `R5Stream.client`. The client will need a method that matches the signature of the return. Since `streams.getLiveStreams` returns a string, a void method with a single string parameter will handle the result. + +```Swift +func onGetLiveStreams (streams : String){ + + var names : NSArray + + do{ + names = try NSJSONSerialization.JSONObjectWithData(streams.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers) as! NSArray + } catch _ { + self.timer = NSTimer(timeInterval: 2, target: self, selector: Selector("getStreams"), userInfo: nil, repeats: false) + return + } + + for i in 0.. +[TwoWayTest.swift #86](TwoWayTest.swift#L86) + + +A simple json parsing will get all streams, and start the timer over to request the streams again. The table view is loaded with the new stream names on the return to display for subscription. diff --git a/R5ProTestbed/Tests/TwoWay/TwoWayTest.swift b/R5ProTestbed/Tests/TwoWay/TwoWayTest.swift new file mode 100644 index 0000000..e7c5954 --- /dev/null +++ b/R5ProTestbed/Tests/TwoWay/TwoWayTest.swift @@ -0,0 +1,113 @@ +// +// TwoWay.swift +// R5ProTestbed +// +// Created by David Heimann on 3/9/16. +// Copyright © 2016 Infrared5. All rights reserved. +// + +import UIKit +import R5Streaming + +@objc(TwoWayTest) +class TwoWayTest: BaseTest { + var publishView : R5VideoViewController? = nil + var timer : NSTimer? = nil + + override func viewDidAppear(animated: Bool) { + + super.viewDidAppear(animated) + + AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in }; + + setupDefaultR5VideoViewController() + + + publishView = getNewR5VideoViewController(self.view.frame); + self.addChildViewController(publishView!); + + view.addSubview(publishView!.view) + + publishView!.showPreview(true) + + publishView!.showDebugInfo(Testbed.getParameter("debug_view") as! Bool) + + // Set up the configuration + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + + setupPublisher(connection) + // show preview and debug info + + publishView!.attachStream(publishStream!) + + let screenSize = UIScreen.mainScreen().bounds.size + let newFrame = CGRectMake( screenSize.width * (3/5), screenSize.height * (3/5), screenSize.width * (2/5), screenSize.height * (2/5) ) + publishView?.view.frame = newFrame + + self.publishStream?.client = self; + self.publishStream!.publish(Testbed.getParameter("stream1") as! String, type: R5RecordTypeLive) + } + + func subscribeBegin() + { + if( subscribeStream == nil ) + { + let config = getConfig() + // Set up the connection and stream + let connection = R5Connection(config: config) + self.subscribeStream = R5Stream(connection: connection) + self.subscribeStream!.delegate = self + self.subscribeStream?.client = self; + + currentView?.attachStream(subscribeStream) + + self.subscribeStream!.play(Testbed.getParameter("stream2") as! String) + } + } + + override func onR5StreamStatus(stream: R5Stream!, withStatus statusCode: Int32, withMessage msg: String!) { + + if(stream == self.publishStream){ + + if(Int(statusCode) == Int(r5_status_start_streaming.rawValue)){ + + self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getStreams"), userInfo: nil, repeats: false) + } + } + } + + func getStreams (){ + publishStream?.connection.call("streams.getLiveStreams", withReturn: "onGetLiveStreams", withParam: nil) + } + + func onGetLiveStreams (streams : String){ + + NSLog("Got streams: " + streams) + + var names : NSArray + + do{ + names = try NSJSONSerialization.JSONObjectWithData(streams.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers) as! NSArray + } catch _ { + self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getStreams"), userInfo: nil, repeats: false) + return + } + + for i in 0.. + + + + GlobalProperties + + bitrate + 750 + host + 0.0.0.0 + buffer_time + 1 + port + 8554 + stream1 + stream1 + stream2 + stream2 + context + live + camera_width + 640 + camera_height + 360 + debug_view + + video_on + + audio_on + + + Tests + + home + + class + Home + name + Home + + publish + + LocalProperties + + name + Publish + class + PublishTest + + publish - 1080p + + description + A high quality publisher + LocalProperties + + camera_width + 1920 + camera_height + 1080 + bitrate + 4500 + + name + Publish - 1080p + class + PublishTest + + publish - ABR + + description + A high quality publisher with AdaptiveBitrateController + LocalProperties + + camera_width + 1920 + camera_height + 1080 + bitrate + 4800 + + name + Publish - ABR + class + AdaptiveBitrateControllerTest + + publish - Camera Swap + + description + Touch the screen to swap which camera is being used! Verify using flash that camera is swapping properly and no rendering problems occur. + LocalProperties + + name + Publish - Camera Swap + class + CameraSwapTest + + publish - Custom Video Source + + description + Touch the screen to swap which camera is being used! Verify using flash that camera is swapping properly and no rendering problems occur. + LocalProperties + + name + Publish - Custom Video Source + class + PublishCustomSourceTest + + publish - Image Capture + + description + Touch the publish stream to take a screen shot that is displayed! + LocalProperties + + name + Publish - ImageCapture + class + PublishStreamImageTest + + publish - Orientation + + description + Touch the screen to rotate the output video 90 degrees. Verify with flash, android, or other iOS device running subscribe test. + LocalProperties + + name + Publish - Orientation Change + class + PublishOrientationTest + + publish - Record + + description + A publish example that records stream data on the server. + LocalProperties + + name + Publish - Record + class + RecordedTest + + publish - Remote Call + + description + The publish portion of the remote call example - sends the remote call + LocalProperties + + name + Publish - Remote Call + class + PublishRemoteCallTest + + publish - Stream Manager + + description + A publish example that connects to a cluster server with the Stream Manager. + LocalProperties + + name + Publish - Stream Manager + class + PublishStreamManagerTest + + subscribe + + LocalProperties + + buffer_time + 0 + + name + Subscribe + class + SubscribeTest + + subscribe - Aspect Ratio + + description + Change the fill mode of the stream. scale to fill, scale to fit, scale fill. Aspect ratio should be maintained on first 2. + LocalProperties + + name + Subscribe - Aspect Ratio + class + SubscribeAspectRatioTest + + subscribe - Cluster + + description + An example of conecting to a cluster server. + LocalProperties + + name + Subscribe - Cluster + class + SubscribeCluster + + subscribe - Image Capture + + description + Touch the subscribe stream to take a screen shot that is displayed! + LocalProperties + + name + Subscribe - ImageCapture + class + SubscribeStreamImageTest + + subscribe - No View + + description + A proof of using an audio only stream without attaching it to a view. + LocalProperties + + name + Subscribe - No View + class + SubscribeNoViewTest + + subscribe - Reconnect + + LocalProperties + + name + Subscribe - Reconnect + class + SubscribeAutoReconnectTest + + subscribe - Remote Call + + description + The subscribe portion of the remote call example - receives the remote call + LocalProperties + + name + Subscribe - Remote Call + class + SubscribeRemoteCallTest + + subscribe - Stream Manager + + description + A subscribe example that connects to a cluster server with the Stream Manager. + LocalProperties + + name + Subscribe - Stream Manager + class + SubscribeStreamManagerTest + + subscribe - Two Streams + + description + Allows subscribing to both streams at once + LocalProperties + + name + Subscribe - Two Streams + class + SubscribeTwoStreams + + TwoWay + + description + Publish and subscribe simultaneously to hold a conversation + LocalProperties + + name + Two Way + class + TwoWayTest + + + + diff --git a/Readme.md b/Readme.md index 52dd991..351e6b4 100644 --- a/Readme.md +++ b/Readme.md @@ -1,16 +1,16 @@ -#Red5 Pro iOS Streaming Examples +# Red5 Pro iOS Streaming Testbed This repository contains a simple project with a number of examples that can be used for testing and reference. ##Requirements -You will need a functional, running Red5 Pro server web- (or locally-) accessible for the client to connect to. +You will need a functional, running Red5 Pro server web- (or locally-) accessible for the client to connect to. -For more information visit http://red5pro.com. +For more information visit [Red5Pro.com](http://red5pro.com). ##Setup -You will need to modify **/Red5ProStreaming/Supporting Files/connection.plist (the domain value)** to point to your server instance. If you do not, the examples will not function when you build. +You will need to modify **/Red5ProTestbed/tests.plist (the domain value)** to point to your server instance's IP address. If you do not, the examples will not function when you build. If you are running the server locally, then your machine and mobile device need to be on the same wifi network. Once you have modified your settings, you can run the application for simulator or device. @@ -18,44 +18,69 @@ Once you have modified your settings, you can run the application for simulator ##Examples +###[Publishing](R5ProTestbed/Tests/Publish) - -###[Publishing](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/Publish) - -| **[Adaptive Bitrate Publishing](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/AdaptiveBitratePublish)** +| **[1080p](R5ProTestbed/Tests/Publish)** | :----- -| *Utilize the AdaptiveBitrateController to dynamically adjust video bitrate with connection quality* +| *A high quality publisher. Note that this is the publish test with a non-default 'bitrate' and camera size values set in tests.plist* +| +| **[ABR](R5ProTestbed/Tests/AdaptiveBitrate)** +| *A high bitrate publisher with AdaptiveBitrateController* +| +| **[Camera Swap](R5ProTestbed/Tests/CameraSwap)** +| *Touch the screen to swap which camera is being used! erify with flash, android, or other iOS device running subscribe test that camera is swapping properly and no rendering problems occur.* | -| **[Custom Video Source](Red5ProStreaming/Examples/CustomVideo)** -| *Publish custom data to a Red5 Pro stream in place of standard camera input* +| **[Custom Video Source](R5ProTestbed/Tests/PublishCustomSource)** +| *Uses a custom controller to supply video data to the publisher.* | -| **[Stream Send](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/StreamSend)** -| *Broadcast messages to subscribers with R5Stream.send - includes example for recieving as a subscriber* +| **[Image Capture](R5ProTestbed/Tests/PublishStreamImage)** +| *Touch the publish stream to take a screen shot that is displayed!* | -| **[Swift Publishing](Red5ProStreaming/Examples/SwiftPublish)** -| *How to publish using Swift* +| **[Orientation](R5ProTestbed/Tests/PublishOrientation)** +| *Touch the screen to rotate the output video 90 degrees. Verify with flash, android, or other iOS device running subscribe test that image is rotating properly and no rendering problems occur.* | -| **[Two Way Video Chat](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/TwoWayVideoChat)** -| *Starter example that shows how to implement a two way video chat using each end as both publisher and subscriber* +| **[Record](R5ProTestbed/Tests/Recorded)** +| *A publish example that records stream data on the server.* +| +| **[Remote Call](R5ProTestbed/Tests/RemoteCall)** +| *The publish portion of the remote call example - sends the remote call.* +| +| **[Stream Manager](R5ProTestbed/Tests/PublishStreamManager)** +| *A publish example that connects with a server cluster using a Stream Manger* +| +| **[Two Way](R5ProTestbed/Tests/TwoWay)** +| *An example of simultaneously publishing while subscribing - allowing a conversation. Includes stream detection and auto-connection.* -###[Subscribing](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/Subscribe) +###[Subscribing](R5ProTestbed/Tests/Subscribe) -| **[AutoReconnect](https://github.com/red5pro/streaming-ios/tree/master/Red5ProStreaming/Examples/AutoReconnect)** -| :----- -| *Wait for a publisher to start by monitoring connection events* +| **[Aspect Ratio](R5ProTestbed/Tests/SubscribeAspectRatio)** +| :---- +| *Change the fill mode of the stream. scale to fill, scale to fit, scale fill. Aspect ratio should be maintained on first 2.* | -| **[Clustering](Red5ProStreaming/Examples/Clustering)** -| *How to subscribe to a Red5 Pro Cluster* +| **[Cluster](R5ProTestbed/Tests/SubscribeCluster)** +| *An example of conecting to a cluster server.* | -| **[Stream Image Capture](Red5ProStreaming/Examples/StreamImage)** -| *Capture an image from a subscribing R5Stream* +| **[Image Capture](R5ProTestbed/Tests/SubscribeStreamImage)** +| *Touch the subscribe stream to take a screen shot that is displayed!* +| +| **[No View](R5ProTestbed/Tests/SubscribeNoView)** +| *A proof of using an audio only stream without attaching it to a view.* +| +| **[Remote Call](R5ProTestbed/Tests/RemoteCall)** +| *The subscribe portion of the remote call example - receives the remote call.* +| +| **[Stream Manager](R5ProTestbed/Tests/SubscribeStreamManager)** +| *A subscribe example that connects with a server cluster using a Stream Manger* +| +| **[Two Streams](R5ProTestbed/Tests/SubscribeTwoStreams)** +| *An example of subscribing to multiple streams at once, useful for subscribing to a presentation hosted by two people using a Two Way connection.* + + ##Notes -1. For some of the above examples you will need two devices (a publisher, and a subscriber). You can also use a web browser to subscribe or publish via Flash. -2. You can see a list of active streams by navigating to http://your_red5_pro_server_ip:5080/live/streams.jsp -3. Click on the flash link (for example, flash_publisher) in the streams list displayed to view the published stream in your browser. - -## Additional Red5 Pro Examples Can be found at: [android-streaming-testbed] (https://github.com/red5pro/android-streaming-testbed) and [ios-streaming-testbed] (https://github.com/red5pro/ios-streaming-testbed) +1. For some of the above examples you will need two devices (a publisher, and a subscriber). You can also use a web browser to subscribe or publish via Flash, http://your_red5_pro_server_ip:5080/live. +2. You can see a list of active streams by navigating to http://your_red5_pro_server_ip:5080/live/subscribe.jsp (will need to refresh this page after you have started publishing). +3. Click on the *flash* link to view the published stream in your browser. -[![Analytics](https://ga-beacon.appspot.com/UA-59819838-3/red5pro/streaming-ios?pixel)](https://github.com/igrigorik/ga-beacon) +[![Analytics](https://ga-beacon.appspot.com/UA-59819838-3/red5pro/streaming-ios?pixel)](https://github.com/igrigorik/ga-beacon) \ No newline at end of file diff --git a/Red5ProStreaming.xcodeproj/project.pbxproj b/Red5ProStreaming.xcodeproj/project.pbxproj deleted file mode 100644 index 7935c4b..0000000 --- a/Red5ProStreaming.xcodeproj/project.pbxproj +++ /dev/null @@ -1,663 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 4812FBD01B3312D10041EA17 /* AutoReconnectExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 4812FBCF1B3312D10041EA17 /* AutoReconnectExample.m */; }; - 4816815D1C03915900ABCBE2 /* CustomVideoSourceExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 4816815C1C03915900ABCBE2 /* CustomVideoSourceExample.m */; }; - 481681631C0399C900ABCBE2 /* ColorsVideoSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 481681621C0399C900ABCBE2 /* ColorsVideoSource.mm */; }; - 4816816A1C03BBBC00ABCBE2 /* SwiftPublishExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 481681691C03BBBC00ABCBE2 /* SwiftPublishExample.swift */; }; - 482CE9AF1B42F67C0050B9B1 /* video_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 482CE9AE1B42F67C0050B9B1 /* video_icon.png */; }; - 482CE9B21B431B870050B9B1 /* StreamSendExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 482CE9B11B431B870050B9B1 /* StreamSendExample.m */; }; - 4834CAE21B31C0A40084335B /* PublishExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 4834CAE11B31C0A40084335B /* PublishExample.m */; }; - 4834CAE81B31C6520084335B /* AdaptiveBitrateExample.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4834CAE61B31C6510084335B /* AdaptiveBitrateExample.mm */; }; - 4834CAED1B31C6900084335B /* ALToastView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4834CAEC1B31C68F0084335B /* ALToastView.m */; }; - 484960151BFA517800969F81 /* StreamImageExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 484960141BFA517800969F81 /* StreamImageExample.m */; }; - 486031331B3350FE00EE08ED /* TwoWayVideoChatExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 486031321B3350FE00EE08ED /* TwoWayVideoChatExample.m */; }; - 48638C1F1B30736400D2355C /* connection.plist in Resources */ = {isa = PBXBuildFile; fileRef = 48638C1E1B30736400D2355C /* connection.plist */; }; - 48638C261B307D3900D2355C /* BaseExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 48638C251B307D3900D2355C /* BaseExample.m */; }; - 48638C281B307D8800D2355C /* R5Streaming.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48638C271B307D8800D2355C /* R5Streaming.framework */; }; - 48638C2A1B307DA000D2355C /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48638C291B307DA000D2355C /* GLKit.framework */; }; - 48638C2C1B307DA600D2355C /* libiconv.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 48638C2B1B307DA600D2355C /* libiconv.2.dylib */; }; - 48638C311B308E9F00D2355C /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48638C301B308E9E00D2355C /* OpenAL.framework */; }; - 4879CB0F1B3072ED00E3702F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4879CB0E1B3072ED00E3702F /* main.m */; }; - 4879CB121B3072ED00E3702F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4879CB111B3072ED00E3702F /* AppDelegate.m */; }; - 4879CB151B3072ED00E3702F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4879CB141B3072ED00E3702F /* ViewController.m */; }; - 4879CB181B3072ED00E3702F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4879CB161B3072ED00E3702F /* Main.storyboard */; }; - 4879CB1A1B3072ED00E3702F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4879CB191B3072ED00E3702F /* Images.xcassets */; }; - 4879CB1D1B3072ED00E3702F /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4879CB1B1B3072ED00E3702F /* LaunchScreen.xib */; }; - 4879CB291B3072ED00E3702F /* Red5ProStreamingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4879CB281B3072ED00E3702F /* Red5ProStreamingTests.m */; }; - 489D6BEB1B31F0B1006403C1 /* SubscribeExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 489D6BEA1B31F0B1006403C1 /* SubscribeExample.m */; }; - C3F0EB1A1C0E355700932574 /* ClusteringExample.m in Sources */ = {isa = PBXBuildFile; fileRef = C3F0EB191C0E355700932574 /* ClusteringExample.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 4879CB231B3072ED00E3702F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4879CB011B3072ED00E3702F /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4879CB081B3072ED00E3702F; - remoteInfo = Red5ProStreaming; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 4812FBCE1B3312D10041EA17 /* AutoReconnectExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoReconnectExample.h; sourceTree = ""; }; - 4812FBCF1B3312D10041EA17 /* AutoReconnectExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutoReconnectExample.m; sourceTree = ""; }; - 4816815B1C03915900ABCBE2 /* CustomVideoSourceExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CustomVideoSourceExample.h; path = Examples/CustomVideo/CustomVideoSourceExample.h; sourceTree = ""; }; - 4816815C1C03915900ABCBE2 /* CustomVideoSourceExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CustomVideoSourceExample.m; path = Examples/CustomVideo/CustomVideoSourceExample.m; sourceTree = ""; }; - 481681611C0399C900ABCBE2 /* ColorsVideoSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ColorsVideoSource.h; path = Examples/CustomVideo/ColorsVideoSource.h; sourceTree = ""; }; - 481681621C0399C900ABCBE2 /* ColorsVideoSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ColorsVideoSource.mm; path = Examples/CustomVideo/ColorsVideoSource.mm; sourceTree = ""; }; - 481681681C03BBBC00ABCBE2 /* Red5ProStreaming-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Red5ProStreaming-Bridging-Header.h"; sourceTree = ""; }; - 481681691C03BBBC00ABCBE2 /* SwiftPublishExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftPublishExample.swift; path = Examples/SwiftPublish/SwiftPublishExample.swift; sourceTree = ""; }; - 482CE9AE1B42F67C0050B9B1 /* video_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = video_icon.png; sourceTree = ""; }; - 482CE9B01B431B870050B9B1 /* StreamSendExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamSendExample.h; path = Red5ProStreaming/Examples/StreamSend/StreamSendExample.h; sourceTree = SOURCE_ROOT; }; - 482CE9B11B431B870050B9B1 /* StreamSendExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StreamSendExample.m; path = Red5ProStreaming/Examples/StreamSend/StreamSendExample.m; sourceTree = SOURCE_ROOT; }; - 4834CAE01B31C0A40084335B /* PublishExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublishExample.h; sourceTree = ""; }; - 4834CAE11B31C0A40084335B /* PublishExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PublishExample.m; sourceTree = ""; }; - 4834CAE51B31C6510084335B /* AdaptiveBitrateExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdaptiveBitrateExample.h; sourceTree = ""; }; - 4834CAE61B31C6510084335B /* AdaptiveBitrateExample.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdaptiveBitrateExample.mm; sourceTree = ""; }; - 4834CAEB1B31C68F0084335B /* ALToastView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALToastView.h; sourceTree = ""; }; - 4834CAEC1B31C68F0084335B /* ALToastView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALToastView.m; sourceTree = ""; }; - 484960131BFA517800969F81 /* StreamImageExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamImageExample.h; path = Examples/StreamImage/StreamImageExample.h; sourceTree = ""; }; - 484960141BFA517800969F81 /* StreamImageExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StreamImageExample.m; path = Examples/StreamImage/StreamImageExample.m; sourceTree = ""; }; - 486031311B3350FE00EE08ED /* TwoWayVideoChatExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwoWayVideoChatExample.h; sourceTree = ""; }; - 486031321B3350FE00EE08ED /* TwoWayVideoChatExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwoWayVideoChatExample.m; sourceTree = ""; }; - 48638C1E1B30736400D2355C /* connection.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = connection.plist; sourceTree = ""; }; - 48638C241B307D3900D2355C /* BaseExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseExample.h; sourceTree = ""; }; - 48638C251B307D3900D2355C /* BaseExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseExample.m; sourceTree = ""; }; - 48638C271B307D8800D2355C /* R5Streaming.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = R5Streaming.framework; sourceTree = ""; }; - 48638C291B307DA000D2355C /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; - 48638C2B1B307DA600D2355C /* libiconv.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.2.dylib; path = usr/lib/libiconv.2.dylib; sourceTree = SDKROOT; }; - 48638C301B308E9E00D2355C /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; - 4879CB091B3072ED00E3702F /* Red5ProStreaming.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Red5ProStreaming.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4879CB0D1B3072ED00E3702F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4879CB0E1B3072ED00E3702F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 4879CB101B3072ED00E3702F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 4879CB111B3072ED00E3702F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 4879CB131B3072ED00E3702F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 4879CB141B3072ED00E3702F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - 4879CB171B3072ED00E3702F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 4879CB191B3072ED00E3702F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 4879CB1C1B3072ED00E3702F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 4879CB221B3072ED00E3702F /* Red5ProStreamingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Red5ProStreamingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 4879CB271B3072ED00E3702F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4879CB281B3072ED00E3702F /* Red5ProStreamingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Red5ProStreamingTests.m; sourceTree = ""; }; - 489D6BE91B31F0B1006403C1 /* SubscribeExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubscribeExample.h; sourceTree = ""; }; - 489D6BEA1B31F0B1006403C1 /* SubscribeExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SubscribeExample.m; sourceTree = ""; }; - C3F0EB181C0E355700932574 /* ClusteringExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClusteringExample.h; sourceTree = ""; }; - C3F0EB191C0E355700932574 /* ClusteringExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ClusteringExample.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4879CB061B3072ED00E3702F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 48638C311B308E9F00D2355C /* OpenAL.framework in Frameworks */, - 48638C2C1B307DA600D2355C /* libiconv.2.dylib in Frameworks */, - 48638C2A1B307DA000D2355C /* GLKit.framework in Frameworks */, - 48638C281B307D8800D2355C /* R5Streaming.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4879CB1F1B3072ED00E3702F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4812FBCD1B3312060041EA17 /* AutoReconnect */ = { - isa = PBXGroup; - children = ( - 4812FBCE1B3312D10041EA17 /* AutoReconnectExample.h */, - 4812FBCF1B3312D10041EA17 /* AutoReconnectExample.m */, - ); - name = AutoReconnect; - path = Examples/AutoReconnect; - sourceTree = ""; - }; - 4816815A1C03912600ABCBE2 /* CustomVideoSource */ = { - isa = PBXGroup; - children = ( - 481681611C0399C900ABCBE2 /* ColorsVideoSource.h */, - 481681621C0399C900ABCBE2 /* ColorsVideoSource.mm */, - 4816815B1C03915900ABCBE2 /* CustomVideoSourceExample.h */, - 4816815C1C03915900ABCBE2 /* CustomVideoSourceExample.m */, - ); - name = CustomVideoSource; - sourceTree = ""; - }; - 481681671C03BB9700ABCBE2 /* SwiftPublish */ = { - isa = PBXGroup; - children = ( - 481681691C03BBBC00ABCBE2 /* SwiftPublishExample.swift */, - ); - name = SwiftPublish; - sourceTree = ""; - }; - 4834CADF1B31C08F0084335B /* Publish */ = { - isa = PBXGroup; - children = ( - 4834CAE01B31C0A40084335B /* PublishExample.h */, - 4834CAE11B31C0A40084335B /* PublishExample.m */, - ); - name = Publish; - path = Examples/Publish; - sourceTree = ""; - }; - 4834CAE41B31C6510084335B /* AdaptiveBitratePublish */ = { - isa = PBXGroup; - children = ( - 4834CAE51B31C6510084335B /* AdaptiveBitrateExample.h */, - 4834CAE61B31C6510084335B /* AdaptiveBitrateExample.mm */, - ); - name = AdaptiveBitratePublish; - path = Examples/AdaptiveBitratePublish; - sourceTree = ""; - }; - 4834CAEA1B31C68F0084335B /* ALToastView */ = { - isa = PBXGroup; - children = ( - 4834CAEB1B31C68F0084335B /* ALToastView.h */, - 4834CAEC1B31C68F0084335B /* ALToastView.m */, - ); - path = ALToastView; - sourceTree = ""; - }; - 484960121BFA514C00969F81 /* StreamImage */ = { - isa = PBXGroup; - children = ( - 484960131BFA517800969F81 /* StreamImageExample.h */, - 484960141BFA517800969F81 /* StreamImageExample.m */, - ); - name = StreamImage; - sourceTree = ""; - }; - 486031301B3350E300EE08ED /* TwoWayVideoChat */ = { - isa = PBXGroup; - children = ( - 486031311B3350FE00EE08ED /* TwoWayVideoChatExample.h */, - 486031321B3350FE00EE08ED /* TwoWayVideoChatExample.m */, - ); - name = TwoWayVideoChat; - path = Examples/TwoWayVideoChat; - sourceTree = ""; - }; - 48638C201B30785000D2355C /* Examples */ = { - isa = PBXGroup; - children = ( - C3F0EB121C0E352C00932574 /* Clustering */, - 481681671C03BB9700ABCBE2 /* SwiftPublish */, - 4816815A1C03912600ABCBE2 /* CustomVideoSource */, - 484960121BFA514C00969F81 /* StreamImage */, - 4866127F1B34858100E88A23 /* StreamSend */, - 486031301B3350E300EE08ED /* TwoWayVideoChat */, - 4812FBCD1B3312060041EA17 /* AutoReconnect */, - 489D6BE81B31F094006403C1 /* Subscribe */, - 4834CAE41B31C6510084335B /* AdaptiveBitratePublish */, - 4834CADF1B31C08F0084335B /* Publish */, - 48638C241B307D3900D2355C /* BaseExample.h */, - 48638C251B307D3900D2355C /* BaseExample.m */, - ); - name = Examples; - sourceTree = ""; - }; - 4866127F1B34858100E88A23 /* StreamSend */ = { - isa = PBXGroup; - children = ( - 482CE9B01B431B870050B9B1 /* StreamSendExample.h */, - 482CE9B11B431B870050B9B1 /* StreamSendExample.m */, - ); - name = StreamSend; - path = Examples/RPC; - sourceTree = ""; - }; - 4879CB001B3072ED00E3702F = { - isa = PBXGroup; - children = ( - 48638C301B308E9E00D2355C /* OpenAL.framework */, - 48638C2B1B307DA600D2355C /* libiconv.2.dylib */, - 48638C291B307DA000D2355C /* GLKit.framework */, - 4879CB0B1B3072ED00E3702F /* Red5ProStreaming */, - 4879CB251B3072ED00E3702F /* Red5ProStreamingTests */, - 4879CB0A1B3072ED00E3702F /* Products */, - ); - sourceTree = ""; - }; - 4879CB0A1B3072ED00E3702F /* Products */ = { - isa = PBXGroup; - children = ( - 4879CB091B3072ED00E3702F /* Red5ProStreaming.app */, - 4879CB221B3072ED00E3702F /* Red5ProStreamingTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 4879CB0B1B3072ED00E3702F /* Red5ProStreaming */ = { - isa = PBXGroup; - children = ( - 481681681C03BBBC00ABCBE2 /* Red5ProStreaming-Bridging-Header.h */, - 4834CAEA1B31C68F0084335B /* ALToastView */, - 48638C271B307D8800D2355C /* R5Streaming.framework */, - 48638C201B30785000D2355C /* Examples */, - 4879CB101B3072ED00E3702F /* AppDelegate.h */, - 4879CB111B3072ED00E3702F /* AppDelegate.m */, - 4879CB131B3072ED00E3702F /* ViewController.h */, - 4879CB141B3072ED00E3702F /* ViewController.m */, - 4879CB161B3072ED00E3702F /* Main.storyboard */, - 4879CB191B3072ED00E3702F /* Images.xcassets */, - 4879CB1B1B3072ED00E3702F /* LaunchScreen.xib */, - 4879CB0C1B3072ED00E3702F /* Supporting Files */, - ); - path = Red5ProStreaming; - sourceTree = ""; - }; - 4879CB0C1B3072ED00E3702F /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 482CE9AE1B42F67C0050B9B1 /* video_icon.png */, - 4879CB0D1B3072ED00E3702F /* Info.plist */, - 4879CB0E1B3072ED00E3702F /* main.m */, - 48638C1E1B30736400D2355C /* connection.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 4879CB251B3072ED00E3702F /* Red5ProStreamingTests */ = { - isa = PBXGroup; - children = ( - 4879CB281B3072ED00E3702F /* Red5ProStreamingTests.m */, - 4879CB261B3072ED00E3702F /* Supporting Files */, - ); - path = Red5ProStreamingTests; - sourceTree = ""; - }; - 4879CB261B3072ED00E3702F /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 4879CB271B3072ED00E3702F /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 489D6BE81B31F094006403C1 /* Subscribe */ = { - isa = PBXGroup; - children = ( - 489D6BE91B31F0B1006403C1 /* SubscribeExample.h */, - 489D6BEA1B31F0B1006403C1 /* SubscribeExample.m */, - ); - name = Subscribe; - path = Examples/Subscribe; - sourceTree = ""; - }; - C3F0EB121C0E352C00932574 /* Clustering */ = { - isa = PBXGroup; - children = ( - C3F0EB181C0E355700932574 /* ClusteringExample.h */, - C3F0EB191C0E355700932574 /* ClusteringExample.m */, - ); - name = Clustering; - path = Examples/Clustering; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4879CB081B3072ED00E3702F /* Red5ProStreaming */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4879CB2C1B3072ED00E3702F /* Build configuration list for PBXNativeTarget "Red5ProStreaming" */; - buildPhases = ( - 4879CB051B3072ED00E3702F /* Sources */, - 4879CB061B3072ED00E3702F /* Frameworks */, - 4879CB071B3072ED00E3702F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Red5ProStreaming; - productName = Red5ProStreaming; - productReference = 4879CB091B3072ED00E3702F /* Red5ProStreaming.app */; - productType = "com.apple.product-type.application"; - }; - 4879CB211B3072ED00E3702F /* Red5ProStreamingTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4879CB2F1B3072ED00E3702F /* Build configuration list for PBXNativeTarget "Red5ProStreamingTests" */; - buildPhases = ( - 4879CB1E1B3072ED00E3702F /* Sources */, - 4879CB1F1B3072ED00E3702F /* Frameworks */, - 4879CB201B3072ED00E3702F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 4879CB241B3072ED00E3702F /* PBXTargetDependency */, - ); - name = Red5ProStreamingTests; - productName = Red5ProStreamingTests; - productReference = 4879CB221B3072ED00E3702F /* Red5ProStreamingTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4879CB011B3072ED00E3702F /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0610; - ORGANIZATIONNAME = Infrared5; - TargetAttributes = { - 4879CB081B3072ED00E3702F = { - CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = E964K44BWW; - LastSwiftMigration = 0800; - }; - 4879CB211B3072ED00E3702F = { - CreatedOnToolsVersion = 6.1.1; - LastSwiftMigration = 0800; - TestTargetID = 4879CB081B3072ED00E3702F; - }; - }; - }; - buildConfigurationList = 4879CB041B3072ED00E3702F /* Build configuration list for PBXProject "Red5ProStreaming" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4879CB001B3072ED00E3702F; - productRefGroup = 4879CB0A1B3072ED00E3702F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4879CB081B3072ED00E3702F /* Red5ProStreaming */, - 4879CB211B3072ED00E3702F /* Red5ProStreamingTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 4879CB071B3072ED00E3702F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4879CB181B3072ED00E3702F /* Main.storyboard in Resources */, - 4879CB1D1B3072ED00E3702F /* LaunchScreen.xib in Resources */, - 48638C1F1B30736400D2355C /* connection.plist in Resources */, - 482CE9AF1B42F67C0050B9B1 /* video_icon.png in Resources */, - 4879CB1A1B3072ED00E3702F /* Images.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4879CB201B3072ED00E3702F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 4879CB051B3072ED00E3702F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4834CAE21B31C0A40084335B /* PublishExample.m in Sources */, - 486031331B3350FE00EE08ED /* TwoWayVideoChatExample.m in Sources */, - 4879CB151B3072ED00E3702F /* ViewController.m in Sources */, - C3F0EB1A1C0E355700932574 /* ClusteringExample.m in Sources */, - 4834CAED1B31C6900084335B /* ALToastView.m in Sources */, - 4816816A1C03BBBC00ABCBE2 /* SwiftPublishExample.swift in Sources */, - 489D6BEB1B31F0B1006403C1 /* SubscribeExample.m in Sources */, - 4834CAE81B31C6520084335B /* AdaptiveBitrateExample.mm in Sources */, - 481681631C0399C900ABCBE2 /* ColorsVideoSource.mm in Sources */, - 484960151BFA517800969F81 /* StreamImageExample.m in Sources */, - 4812FBD01B3312D10041EA17 /* AutoReconnectExample.m in Sources */, - 48638C261B307D3900D2355C /* BaseExample.m in Sources */, - 482CE9B21B431B870050B9B1 /* StreamSendExample.m in Sources */, - 4879CB121B3072ED00E3702F /* AppDelegate.m in Sources */, - 4816815D1C03915900ABCBE2 /* CustomVideoSourceExample.m in Sources */, - 4879CB0F1B3072ED00E3702F /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4879CB1E1B3072ED00E3702F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4879CB291B3072ED00E3702F /* Red5ProStreamingTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 4879CB241B3072ED00E3702F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4879CB081B3072ED00E3702F /* Red5ProStreaming */; - targetProxy = 4879CB231B3072ED00E3702F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 4879CB161B3072ED00E3702F /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4879CB171B3072ED00E3702F /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 4879CB1B1B3072ED00E3702F /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 4879CB1C1B3072ED00E3702F /* Base */, - ); - name = LaunchScreen.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 4879CB2A1B3072ED00E3702F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 4879CB2B1B3072ED00E3702F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.1; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 4879CB2D1B3072ED00E3702F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = E964K44BWW; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Red5ProStreaming", - ); - INFOPLIST_FILE = Red5ProStreaming/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Red5ProStreaming/Red5ProStreaming-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; - }; - name = Debug; - }; - 4879CB2E1B3072ED00E3702F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - DEVELOPMENT_TEAM = E964K44BWW; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Red5ProStreaming", - ); - INFOPLIST_FILE = Red5ProStreaming/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Red5ProStreaming/Red5ProStreaming-Bridging-Header.h"; - SWIFT_VERSION = 2.3; - }; - name = Release; - }; - 4879CB301B3072ED00E3702F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Red5ProStreamingTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Red5ProStreaming.app/Red5ProStreaming"; - }; - name = Debug; - }; - 4879CB311B3072ED00E3702F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = Red5ProStreamingTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Red5ProStreaming.app/Red5ProStreaming"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 4879CB041B3072ED00E3702F /* Build configuration list for PBXProject "Red5ProStreaming" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4879CB2A1B3072ED00E3702F /* Debug */, - 4879CB2B1B3072ED00E3702F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4879CB2C1B3072ED00E3702F /* Build configuration list for PBXNativeTarget "Red5ProStreaming" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4879CB2D1B3072ED00E3702F /* Debug */, - 4879CB2E1B3072ED00E3702F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4879CB2F1B3072ED00E3702F /* Build configuration list for PBXNativeTarget "Red5ProStreamingTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4879CB301B3072ED00E3702F /* Debug */, - 4879CB311B3072ED00E3702F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4879CB011B3072ED00E3702F /* Project object */; -} diff --git a/Red5ProStreaming/AppDelegate.h b/Red5ProStreaming/AppDelegate.h deleted file mode 100644 index ebd0619..0000000 --- a/Red5ProStreaming/AppDelegate.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// AppDelegate.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - - -@end - diff --git a/Red5ProStreaming/AppDelegate.m b/Red5ProStreaming/AppDelegate.m deleted file mode 100644 index 669fb32..0000000 --- a/Red5ProStreaming/AppDelegate.m +++ /dev/null @@ -1,45 +0,0 @@ -// -// AppDelegate.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "AppDelegate.h" - -@interface AppDelegate () - -@end - -@implementation AppDelegate - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -@end diff --git a/Red5ProStreaming/Base.lproj/LaunchScreen.xib b/Red5ProStreaming/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 22960e6..0000000 --- a/Red5ProStreaming/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Red5ProStreaming/Base.lproj/Main.storyboard b/Red5ProStreaming/Base.lproj/Main.storyboard deleted file mode 100644 index 8ec0c2b..0000000 --- a/Red5ProStreaming/Base.lproj/Main.storyboard +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Red5ProStreaming/BaseExample.h b/Red5ProStreaming/BaseExample.h deleted file mode 100644 index 0b2a72b..0000000 --- a/Red5ProStreaming/BaseExample.h +++ /dev/null @@ -1,63 +0,0 @@ -// -// BaseExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import -#import -#import - - -enum R5StreamType{ PUBLISH, SUBSCRIBE }; - -@interface BaseExample : UIViewController - -@property R5Stream *subscribe; -@property R5Stream *publish; -@property R5VideoViewController *r5View; - -/** - * A new connection object populated from connection.plist - * - * @return a new connection - */ --(R5Stream *) getNewStream: (enum R5StreamType) type; - -/** - * Get the name of the stream from the connection.plist - * - * @param publishing is this a publish or subscribe stream - * - * @return name for the stream - */ --(NSString*)getStreamName : (enum R5StreamType)type; - -/** - * Close all R5Streams - */ --(void) cleanup; - - -/** - * Get a new R5VideoViewController to display on - * - * @param frame view frame to use - * - * @return View Controller setup - */ --(R5VideoViewController *) getNewViewController: (CGRect) frame; - -/** - * Setup the default R5View as a fullscreen view - */ --(void) setupDefaultR5ViewController; - - -//swap stream names to test on 2 devices together -+(void)setSwapped:(BOOL)swapped; -+(BOOL)getSwapped; - -@end diff --git a/Red5ProStreaming/BaseExample.m b/Red5ProStreaming/BaseExample.m deleted file mode 100644 index a231f8b..0000000 --- a/Red5ProStreaming/BaseExample.m +++ /dev/null @@ -1,149 +0,0 @@ -// -// BaseExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" -#import "ALToastView.h" - -//simple static to mark whether we shoudl switch stream1 and stream2 names - used for testing 2 devices together -static BOOL _swapped = NO; - - -@implementation BaseExample - - -+(void)setSwapped:(BOOL)swapped{ - _swapped = swapped; -} - -+(BOOL)getSwapped{ - return _swapped; -} - --(void)viewDidLoad{ - - - [self setEdgesForExtendedLayout:UIRectEdgeAll]; - -} - - --(R5Stream *) getNewStream: (enum R5StreamType) type{ - NSLog(@"Got the stream"); - r5_set_log_level(r5_log_level_debug); - - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - R5Configuration *config = [[R5Configuration alloc] init]; - config.host = [dict objectForKey:@"domain"]; - config.contextName = [dict objectForKey:@"context"]; - config.port = [(NSNumber *)[dict objectForKey:@"port"] intValue]; - config.protocol = 1; - config.buffer_time = 1; - - R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; - - R5Stream *stream = [[R5Stream alloc] initWithConnection:connection]; - - - stream.delegate = self; - - //attach audio/video to stream if we are publishing! - if(type == PUBLISH){ - - if([[dict objectForKey:@"showVideo"] boolValue] == YES){ - - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - AVCaptureDevice *videoDev = [devices lastObject]; - - R5Camera *camera = [[R5Camera alloc] initWithDevice:videoDev andBitRate:750]; - - camera.width = 640; - camera.height = 360; - - camera.orientation = 90; - - [stream attachVideo:camera]; - - } - - AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeAudio]; - - R5Microphone *microphone = [[R5Microphone new] initWithDevice:audioDevice]; - microphone.bitrate = 32; - - [stream attachAudio:microphone]; - - - } - - return stream; -} - - --(void) setupDefaultR5ViewController{ - - self.r5View = [self getNewViewController:self.view.frame]; - [self addChildViewController:self.r5View]; - [self.view addSubview:self.r5View.view]; - - //show the camera before we start! - [self.r5View showPreview:YES]; - - //show the debug information for the stream - [self.r5View showDebugInfo:YES]; -} - --(R5VideoViewController *) getNewViewController: (CGRect) frame{ - - UIView *view = [[UIView alloc] initWithFrame: frame]; - R5VideoViewController *viewController = [[R5VideoViewController alloc] init]; - viewController.view = view; - return viewController; -} - - --(NSString*)getStreamName : (enum R5StreamType)type{ - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - if(type == PUBLISH ){ - if(_swapped == NO) - return [dict objectForKey:@"stream1"]; - else - return [dict objectForKey:@"stream2"]; - }else{ - if(_swapped == YES) - return [dict objectForKey:@"stream2"]; - else - return [dict objectForKey:@"stream1"]; - } -} - --(void) cleanup{ - - if(self.publish) - [self.publish stop]; - - if(self.subscribe) - [self.subscribe stop]; -} - - --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - //Display simple popup message with the event information - [ALToastView toastInView:[[[UIApplication sharedApplication] keyWindow] rootViewController].view withText:[NSString stringWithFormat:@"Stream: %s - %@", r5_string_for_status(statusCode), msg]]; - -} - --(void)viewDidDisappear:(BOOL)animated{ - - [self cleanup]; -} - - -@end diff --git a/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.h b/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.h deleted file mode 100644 index c935209..0000000 --- a/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// AdaptiveBitrateExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import -#import -#import "BaseExample.h" - -@interface AdaptiveBitrateExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm b/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm deleted file mode 100644 index 2939cb9..0000000 --- a/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm +++ /dev/null @@ -1,49 +0,0 @@ -// -// AdaptiveBitrateExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "AdaptiveBitrateExample.h" - -@implementation AdaptiveBitrateExample - --(void)viewDidAppear:(BOOL)animated{ - - //set up the publisher - self.publish = [self getNewStream:PUBLISH]; - - //setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - //attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.publish]; - - //before we attach our bitrate controller, we can set our bitrate to be a higher quality - //this can be set directly on the R5Camera in BaseExample::getNewStream as well. - [self.publish getVideoSource].bitrate = 768; - - //setup the adaptive bitrate! - R5AdaptiveBitrateController *adaptor = [R5AdaptiveBitrateController new]; - [adaptor attachToStream:self.publish]; - - //If you do not require video to be published, video may be dropped if network conditions cannot handle the lowest quality video setting. - //If you DO require video, you can use this flag to force it to stay on. WARNING: your video may start artifacting or cause other delays in the stream with this option forced. - adaptor.requiresVideo = YES; - - //start publishing! - [self.publish publish:[self getStreamName:PUBLISH] type:R5RecordTypeLive]; - -} - --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; - -} - - - -@end diff --git a/Red5ProStreaming/Examples/AdaptiveBitratePublish/Readme.md b/Red5ProStreaming/Examples/AdaptiveBitratePublish/Readme.md deleted file mode 100644 index ecdcd05..0000000 --- a/Red5ProStreaming/Examples/AdaptiveBitratePublish/Readme.md +++ /dev/null @@ -1,48 +0,0 @@ -#Adaptive Bitrate Publishing - -This example demonstrates the AdaptiveBitrateController, which provides a mechanism to dynamically adjust the video publishing bitrate to adjust quality to meet the bandwidth restrictions of the network connection or encoding hardware. - -###Example Code -- ***[AdaptiveBitrateExample.mm]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - -###Setup -The AdaptiveBitrateController is simple to setup. You simply create a new instance of the controller and attach the stream you wish to control. It will monitor the stream and make all adjustments automatically for you. - - -```Objective-C -R5AdaptiveBitrateController *adaptor = [R5AdaptiveBitrateController new]; -[adaptor attachToStream:self.publish]; -``` - - -[AdaptiveBitrateExample.mm #29](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm#L29) - - -The controller will continuously adjust the video bitrate until the stream has closed. - -###Range -The AdaptiveBitrateController will dynamically adjust the video bitrate between the lowest possible bitrate the encoder can encode at, and the value set on the R5VideoSource (typically an R5Camera) on the stream. - -```[self.publish getVideoSource].bitrate = 768; -``` - - -[AdaptiveBitrateExample.mm #24](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/AdaptiveBitratePublish/AdaptiveBitrateExample.mm#L24) - - - -The controller will adjust the bitrate ~200 kbps every 2 seconds to achieve the best possible video quality. - - - -Video will be turned off if the stream is unable to maintain a smooth connection at the lowest possible bitrate. You can force video to be included with the `AdaptiveBitrateController.requiresVideo` flag. - - - - - - diff --git a/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.h b/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.h deleted file mode 100644 index 3c6ecbe..0000000 --- a/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// AutoReconnectExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/18/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" - -@interface AutoReconnectExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.m b/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.m deleted file mode 100644 index 937e569..0000000 --- a/Red5ProStreaming/Examples/AutoReconnect/AutoReconnectExample.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// AutoReconnectExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/18/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "AutoReconnectExample.h" - -@interface AutoReconnectExample () -@property NSTimer *timer; -@end - -@implementation AutoReconnectExample - --(void)viewDidAppear:(BOOL)animated{ - - //setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - [self startSubscribe]; - -} - --(void) startSubscribe{ - - //set up the subscriber - short method - self.subscribe = [self getNewStream:SUBSCRIBE]; - - //attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.subscribe]; - - //start the subscriber! - [self.subscribe play:[self getStreamName:SUBSCRIBE]]; - -} - --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - //if we error out - reconnect! - if(statusCode == r5_status_connection_error){ - - self.timer = [NSTimer scheduledTimerWithTimeInterval:8 target:self selector:@selector(reconnect:) userInfo:nil repeats:NO]; - } - - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; -} - --(void)reconnect:(NSTimer*)timer{ - [self startSubscribe]; -} - - --(void) viewWillDisappear:(BOOL)animated{ - - //cancel the timer - if(self.timer != nil){ - [self.timer invalidate]; - self.timer = nil; - } -} - -@end diff --git a/Red5ProStreaming/Examples/AutoReconnect/Readme.md b/Red5ProStreaming/Examples/AutoReconnect/Readme.md deleted file mode 100644 index f1e2798..0000000 --- a/Red5ProStreaming/Examples/AutoReconnect/Readme.md +++ /dev/null @@ -1,42 +0,0 @@ -#Auto Reconnection and Events - -This example demonstrates using the status updates from `R5Stream`. A timer is used to attempt to reconnect to a stream that has not yet been published. - -To use the example: -1. Launch the Auto Reconnect feature in the app and you will see it attempting to connect to stream 'subscriber'. -2. Launch a second device using the publisher app, and use the "swap names" button to publish a stream with the name "subscriber" (alternatively, you can publish via the flash client running on your Red5 Pro server, at http://your_red5_pro_server_ip:5080/live/broadcast.jsp ) -3. After you have started the broadcast, your app will successfully connect to the active stream. - - - -###Example Code -- ***[AutoReconnectExample.m](/AutoReconnectExample.m)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - - -##Handling Status Updates from R5Stream -When the `R5Stream.delegate` is set, all events will be sent through the `R5StreamDelegate.onR5SteamStatus: withStatus: withMessage:` method. - -The status code passed in is an `r5_status` enum that can be seen [here](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/R5Streaming.framework/Headers/global.h#L93). - -This example receives the status event, and if there was a connection error, will start a timer to reconnect the stream in 8 seconds. The message included with the error will display in a toast in the super method. - -```Objective-C --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - //if we error out - reconnect! - if(statusCode == r5_status_connection_error){ - - self.timer = [NSTimer scheduledTimerWithTimeInterval:8 target:self selector:@selector(reconnect:) userInfo:nil repeats:NO]; - } - - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; -} - -``` - -[AutoReconnectExample.m #39](/AutoReconnectExample.m#L39) - - diff --git a/Red5ProStreaming/Examples/Clustering/ClusteringExample.h b/Red5ProStreaming/Examples/Clustering/ClusteringExample.h deleted file mode 100644 index 4481f41..0000000 --- a/Red5ProStreaming/Examples/Clustering/ClusteringExample.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// SubscribeExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" - -@interface ClusteringExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/Clustering/ClusteringExample.m b/Red5ProStreaming/Examples/Clustering/ClusteringExample.m deleted file mode 100644 index f37d5d1..0000000 --- a/Red5ProStreaming/Examples/Clustering/ClusteringExample.m +++ /dev/null @@ -1,108 +0,0 @@ -// -// SubscribeExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "ClusteringExample.h" - -@interface ClusteringExample () - -@end - -@implementation ClusteringExample - --(void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - // Add an informational label - CGRect frame = self.view.frame; - CGRect labelFrame = CGRectMake(10.0, frame.size.height - 30.0, frame.size.width - 20.0, 20.0); - UILabel *label = [[UILabel alloc] initWithFrame:labelFrame]; - [label setTextColor:[UIColor whiteColor]]; - [label setBackgroundColor:[UIColor clearColor]]; - [label setText:@"Fetching..."]; - [self.view addSubview:label]; - - // Get our connection settings - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - // Get the URL from which we will retrieve our connection IP - NSString *domain = [dict objectForKey:@"domain"]; - NSString *urlAsString = [NSString stringWithFormat:@"http://%@:5080/cluster", domain]; - NSURL *url = [NSURL URLWithString:urlAsString]; - - // Connect to the URL above, retrieve the connection IP - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] - queue:[NSOperationQueue new] - completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { - if (error) { - // Handle any errors - [label setText:@"There was an error!"]; - [label setNeedsLayout]; - [label setNeedsDisplay]; - NSLog(@"%@", error); - return; - } - - // Convert our response to a usable NSString - NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - - // The string above is formatted like 99.98.97.96:1234, but we won't need the port portion - NSString *ip = [dataAsString substringToIndex:[dataAsString rangeOfString:@":"].location]; - NSLog(@"Retrieved %@ from %@, of which the usable IP is %@", dataAsString, urlAsString, ip); - - // Setup a configuration object for our connection - R5Configuration *config = [[R5Configuration alloc] init]; - config.host = ip; - config.contextName = [dict objectForKey:@"context"]; - config.port = [(NSNumber *)[dict objectForKey:@"port"] intValue]; - config.protocol = 1; - - // How long to stream the buffer before beginning playback - config.buffer_time = 1; - - // Create a new connection using the configuration above - R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; - - // UI updates must be asynchronous - dispatch_async(dispatch_get_main_queue(), ^{ - // Create our new stream that will utilize that connection - self.subscribe = [[R5Stream alloc] initWithConnection:connection]; - - // Setup our listener to handle events from this stream - self.subscribe.delegate = self; - - // Setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - // Attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.subscribe]; - - // Start subscribing!! - [self.subscribe play:[self getStreamName:SUBSCRIBE]]; - - // Showcase the IP we received, for testing purposes - [label setText:ip]; - [self.view addSubview:label]; - }); - - - }]; -} - -/** - * Handle self.subscribe.delegate callbacks - * - * @param stream stream making callback - * @param statusCode code of message - * @param msg addtional context - */ --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg { - //pass to super to present toast - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; -} - -@end diff --git a/Red5ProStreaming/Examples/Clustering/README.md b/Red5ProStreaming/Examples/Clustering/README.md deleted file mode 100644 index 35ec34b..0000000 --- a/Red5ProStreaming/Examples/Clustering/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Subscribing to a Red5 Pro Cluster - - - -1. [Reference](#reference) -2. [Requirements](#requirements) -3. [Explanation](#explanation) - 1. [Connect to your Red5 Pro Cluster](#connect-to-your-red5-pro-cluster) - 2. [Create your `R5Configuration`](#create-your-r5configuration) - 3. [Create your `R5Connection`](#create-your-r5connection) - 4. [Create your `R5Stream`](#create-your-r5stream) - 5. [Assign your `R5Stream` a delegate](#assign-your-r5stream-a-delegate) - 6. [Attach your `R5Stream` to an `R5VideoViewController`](#attach-your-r5stream-to-an-r5videoviewcontroller) - 7. [Subscribe to your `R5Stream`](#subscribe-to-your-r5stream) - - - -## Reference - -This example was built off of our [Subscribe example](../Subscribe/ "Red5 Pro iOS Subscribe Example"), so please see it for any explanation you find lacking within this document. - -## Requirements - -For this and our [other examples](../ "Red5 Pro iOS Examples"), you will have to edit the base [connection.plist](../../connection.plist "A Red5 Pro configuration dictionary") with appropriate values to suit your own [Red5 Pro](https://red5pro.com/) server and stream(s). - -## Explanation - -##### Connect to your Red5 Pro Cluster -Sending a GET request to the Red5 Pro Cluster origin IP's `/cluster` endpoint on port 5080 (the default port) will return an IP with an attached port. For the purposes of subscribing, one only needs to [use the IP portion of the return](./ClusteringExample.m#L51-L54). - -```objc -NSString *domain = @"99.98.97.96"; -NSString *urlAsString = [NSString stringWithFormat:@"http://%@:5080/cluster", domain]; -NSURL *url = [NSURL URLWithString:urlAsString]; - -[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] - queue:[NSOperationQueue new] - completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { - // ... - NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *ip = [dataAsString substringToIndex:[dataAsString rangeOfString:@":"].location]; - // ... - }]; -``` -[Based on this code](./ClusteringExample.m#L33-L54 "Connecting to your Red5 Pro Cluster origin") - -##### Create your `R5Configuration` -Just as one would for the preliminary [Subscribe example](../Subscribe/), create a configuration with values appropriate to your [Red5 Pro](https://red5pro.com/) server and stream(s). These values include your host (_the IP you receive_), the context name (_e.g. "live"_), what port you're using (_8554 by default_), and your stream name. - -```objc -R5Configuration *config = [[R5Configuration alloc] init]; -config.host = ip; -config.contextName = @"myContext"; -config.port = 8554; -config.protocol = 1; -config.buffer_time = 1; -``` -[Based on this code](./ClusteringExample.m#L64-L71 "Creating an R5Configuration for Red5 Pro") - -##### Create your `R5Connection` -Using the `R5Configuration` you've setup, you can create a connection to your [Red5 Pro](https://red5pro.com/) server. - -```objc -R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; -``` -[Based on this code](./ClusteringExample.m#L74 "Creating an R5Connection for Red5 Pro") - -##### Create your `R5Stream` -Using the `R5Connection` you've setup, you can create a stream connection to your [Red5 Pro](https://red5pro.com/) server. This stream connection will be what sends messages to your delegate as well as what you attach to your video view controller and tell to play. - -```objc -R5Stream *stream = [[R5Stream alloc] initWithConnection:connection]; -``` -[Based on this code](./ClusteringExample.m#L79 "Creating an R5Stream for Red5 Pro") - -##### Assign your `R5Stream` a delegate -The delegate you assign to your `R5Stream` will receive and handle, as you see fit, messages from the `R5Stream` during it's connection and subscription. - -```objc -stream.delegate = self; -``` -[Based on this code](./ClusteringExample.m#L82 "Assigning a delegate to a Red5 Pro R5Stream") - -##### Attach your `R5Stream` to an `R5VideoViewController` -This view controller is what is added to the screen so as to allow visual and audio playback of your stream. - -```objc -// R5VideoViewController *r5view = ... -[r5View attachStream:stream]; -``` -[Based on this code](./ClusteringExample.m#L85-L88 "Attaching an R5Stream to an R5VideoViewController for Red5 Pro") - -##### Subscribe to your `R5Stream` -Setting your `R5Stream` to play will start the visual and audio playback of your stream. - -```objc -[stream play:[self getStreamName:SUBSCRIBE]]; -``` -[Based on this code](./ClusteringExample.m#L91 "Subscribing to a stream on a Red5 Pro Cluster") diff --git a/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.h b/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.h deleted file mode 100644 index 0e32709..0000000 --- a/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ColorsVideoSource.h -// Red5Pro -// -// Created by Andy Zupko on 11/17/15. -// Copyright © 2015 Andy Zupko. All rights reserved. -// - -#import - -@interface ColorsVideoSource : R5VideoSource -@property CMTime frameDuration; -@property CMTime PTS; -@property NSTimer *timer; -@end diff --git a/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.mm b/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.mm deleted file mode 100644 index 640e981..0000000 --- a/Red5ProStreaming/Examples/CustomVideo/ColorsVideoSource.mm +++ /dev/null @@ -1,157 +0,0 @@ -// -// ColorsVideoSource.m -// Red5Pro -// -// Created by Andy Zupko on 11/17/15. -// Copyright © 2015 Infrared5. All rights reserved. -// - -#import "ColorsVideoSource.h" - -@implementation ColorsVideoSource - --(instancetype)init{ - - if((self = [super init]) != nil){ - - //initialize the properties used by the stream! - self.bitrate = 256; - self.width = 320; - self.height = 240; - self.orientation = 0; - self.fps = 15; - - //setup simple timestamp calculation - self.frameDuration = CMTimeMakeWithSeconds(0.1f, self.fps); - self.PTS = kCMTimeZero; - - } - - return self; -} - --(void)startVideoCapture{ - - //start a timer to run at desired framerate. - self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f/self.fps - target:self - selector:@selector(capturePixels:) - userInfo:nil - repeats:YES]; - - -} - --(void)stopVideoCapture{ - - //stop the capture! - [self.timer invalidate]; -} - - - --(void)capturePixels:(NSTimer *)timer{ - - //make sure encoding layer is ready for input! - if(self.encoder != nil){ - - CGSize frameSize = CGSizeMake(352, 288); - - // - // Below is a simple "plasma" style rendering using the PTS as the animation offset - // Using RGB color format - 3 bytes per pixel - // - - float time = CMTimeGetSeconds(self.PTS) * 0.6f; - - float scale = 0.035f; - - int componentsPerPixel = 3; - - uint8_t *rgbpixels = (uint8_t*)malloc(frameSize.width*frameSize.height*componentsPerPixel); - - //stride - int bpr = frameSize.width*componentsPerPixel; - - for(int y = 0;y -[ColorsVideoSource.mm #24](ColorsVideoSource.mm#24) - - -To handle the processing of custom data, you can overwrite `R5VideoSource.startVideoCapture`. This method is called by the stream when it is ready for video content. For this example, we will start a simple timer to call `capturePixels` at our desired framerate. - -``` --(void)startVideoCapture{ - -//start a timer to run at desired framerate. -self.timer = [NSTimer scheduledTimerWithTimeInterval: - 1.0f/self.fps - target:self - selector:@selector(capturePixels:) - userInfo:nil - repeats:YES]; - -} - -``` - -[ColorsVideoSource.mm #33](ColorsVideoSource.mm#33) - - -Handle the stop of the rendering call also be handled in `R5VideoSource.stopVideoCapture`. - -``` --(void)stopVideoCapture{ - - //stop the capture! - [self.timer invalidate]; - -} -``` - -[ColorsVideoSource.mm #45](ColorsVideoSource.mm#45) - - -The last thing to do is pass a `CVPixelBufferRef` to the encoder. `capturePixels:` is called by the timer at the interval specified. - -There is some additional math that won't be covered in this example, so it will be skipped over. - -The first thing that is needed is an array of bytes that will hold the pixel data. This example will be using RGB data, so there are 3 bytes per pixel. - -``` --(void)capturePixels:(NSTimer *)timer{ - -... - -int componentsPerPixel = 3; - -uint8_t *rgbpixels = (uint8_t*)malloc(frameSize.width*frameSize.height*componentsPerPixel); -``` - -[ColorsVideoSource.mm #53](ColorsVideoSource.mm#53) - - -Each pixel can then be assigned a color (between 0-255 in this case) for the RGB channels. - -``` -//Set the R, G, B channels to the desired color -rgbpixels[(y * bpr)+x] = sinf(v*M_PI) * UINT8_MAX; -rgbpixels[(y * bpr)+x+1] = cosf(v*M_PI)* UINT8_MAX ; -rgbpixels[(y * bpr)+x+2] = 0; -``` - -[ColorsVideoSource.mm #91](ColorsVideoSource.mm#91) - - -Once the bytes have been set to their desired RGB values, we create a `CVPixelBufferRef` that will hold that date for processing. - -``` - // Create a pixel buffer -CVPixelBufferRef pixelBuffer = NULL; - -//Create a pixel buffer to hold our bytes with RGB format -OSStatus result = CVPixelBufferCreateWithBytes( - kCFAllocatorDefault, - frameSize.width, - frameSize.height, - kCVPixelFormatType_24RGB, - rgbpixels, - frameSize.width * (componentsPerPixel), - NULL, - (void*)pixelBuffer, - NULL, - &pixelBuffer); -``` - -[ColorsVideoSource.mm #100](ColorsVideoSource.mm#100) - - -Next, we can create the buffer information that is needed. The video format information can be extracted from the pixel buffer, and the only timing information that is required is the timestamp. - -``` -CMVideoFormatDescriptionRef videoInfo = NULL; - -//Create a description for the pixel buffer -result = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, &videoInfo); - -if(result != kCVReturnSuccess) - NSLog(@"Failed to create video info"); - -//Only PTS is needed for the encoder - leave everything else invalid if you want -CMSampleTimingInfo timingInfo = kCMTimingInfoInvalid; - timingInfo.duration = kCMTimeInvalid; - timingInfo.decodeTimeStamp = kCMTimeInvalid; - timingInfo.presentationTimeStamp = self.PTS; -``` - -[ColorsVideoSource.mm #112](ColorsVideoSource.mm#112) - - -Next, we can create our `CMSampleBufferRef` using the `CVPixelBufferRef`, `videoInfo`, and `timingInfo`. - -``` -CMSampleBufferRef buffer = NULL; - -//Create the sample buffer for the pixel buffer! -result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, - pixelBuffer, - true, - NULL, - NULL, videoInfo, &timingInfo, &buffer); -``` - -[ColorsVideoSource.mm #127](ColorsVideoSource.mm#127) - - -The last thing to do is pass the buffer to our encoder, which is part of the `R5VideoSource`, and to increment our timestamp and free up all memory. - -``` - //push the sample buffer to the encoder with type r5_media_type_video_custom -if(!self.pauseEncoding) - [self.encoder encodeFrame:buffer - ofType:r5_media_type_video_custom]; - -//increment our timestamp! -self.PTS = CMTimeAdd(self.PTS, self.frameDuration); - -//free all our content! -CFRelease(buffer); -CFRelease(pixelBuffer); -free(rgbpixels); - -} -``` - -[ColorsVideoSource.mm #137](ColorsVideoSource.mm#137) - - - -***It is very important to remember to use `r5_media_type_video_custom` to pass this buffer into the encoder. The standard video format expects a CVImageBufferRef and will fail to encode with custom pixel data!*** - - -This is all that is needed to pass in RGB pixel data in place of the built in camera. The last thing we have to do is pass this object to the `R5Stream` - -``` -//create a new video source which will pump video to the stream - ColorsVideoSource *customVideoSource = [ColorsVideoSource new]; - -[self.publish attachVideo:customVideoSource]; - -``` - -[CustomVideoSourceExample.m #42](CustomVideoSourceExample.m#42) - \ No newline at end of file diff --git a/Red5ProStreaming/Examples/Publish/PublishExample.h b/Red5ProStreaming/Examples/Publish/PublishExample.h deleted file mode 100644 index 863753b..0000000 --- a/Red5ProStreaming/Examples/Publish/PublishExample.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// PublishExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import -#import "BaseExample.h" - -@interface PublishExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/Publish/PublishExample.m b/Red5ProStreaming/Examples/Publish/PublishExample.m deleted file mode 100644 index 141fe75..0000000 --- a/Red5ProStreaming/Examples/Publish/PublishExample.m +++ /dev/null @@ -1,87 +0,0 @@ -// -// PublishExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "PublishExample.h" - -@implementation PublishExample - - --(void)viewDidAppear:(BOOL)animated{ - - //set up the publisher - short method - //self.publish = [self getNewStream:PUBLISH]; - - - /* - * - Step by step - * - */ - - //Get our connection settings - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - //Setup a configuration object for our connection - R5Configuration *config = [[R5Configuration alloc] init]; - config.host = [dict objectForKey:@"domain"]; - config.contextName = [dict objectForKey:@"context"]; - config.port = [(NSNumber *)[dict objectForKey:@"port"] intValue]; - config.protocol = 1; - config.buffer_time = 1; - - //Create a new connection using the configuration above - R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; - - //Create our new stream that will utilize that connection - self.publish = [[R5Stream alloc] initWithConnection:connection]; - - //Setup our listener to handle events from this stream - self.publish.delegate = self; - - //Get a list of available cameras for this device - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - - //Use the last device (front facing) - AVCaptureDevice *videoDev = [devices lastObject]; - - //Create an R5Camera with that device and specify the max bitrate to allow - //Note : This bitrate will not be respected if it is lower than the encoder can go! - R5Camera *camera = [[R5Camera alloc] initWithDevice:videoDev andBitRate:750]; - - //Set up the resolution we want this camera to use. This can only be set before publishing begins - camera.width = 640; - camera.height = 480; - - //Setup the rotation of the video stream. This is meta data, and is used by the client to rotate the video. No rotation is done on the publisher. - camera.orientation = 90; - - //Add the camera to the stream - [self.publish attachVideo:camera]; - - //Get our audio capture device - AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeAudio]; - - //Setup a new R5Microphone for streaming audio with that device - R5Microphone *microphone = [[R5Microphone new] initWithDevice:audioDevice]; - microphone.bitrate = 32; - - //Attach the microphone to the stream - [self.publish attachAudio:microphone]; - - //setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - //attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.publish]; - - //start publishing! - [self.publish publish:[self getStreamName:PUBLISH] type:R5RecordTypeLive]; - -} - -@end diff --git a/Red5ProStreaming/Examples/Publish/Readme.md b/Red5ProStreaming/Examples/Publish/Readme.md deleted file mode 100644 index 9d07087..0000000 --- a/Red5ProStreaming/Examples/Publish/Readme.md +++ /dev/null @@ -1,162 +0,0 @@ -#Publishing on Red5 Pro - -This is the basic starter example on publishing to a Red5 Pro stream. - -###Example Code -- ***[PublishExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Publish/PublishExample.m)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - - -##How to Publish -Publishing to a Red5 Pro stream requires a few components to function fully. -####Setup R5Connection -The R5Connection manages the connection that the stream utilizes. You will need to setup a configuration and intialize a new connection. - -```Objective-C - //Setup a configuration object for our connection - R5Configuration *config = [[R5Configuration alloc] init]; - config.host = [dict objectForKey:@"domain"]; - config.contextName = [dict objectForKey:@"context"]; - config.port = [(NSNumber *)[dict objectForKey:@"port"] intValue]; - config.protocol = 1; - config.buffer_time = 1; - - //Create a new connection using the configuration above - R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; -``` - -[PublishExample.m #29](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Publish/PublishExample.m#L29) - - -####Setup R5Stream -The `R5Stream` handles both subscribing and publishing. Creating one simply requires the connection already created. - -```Objective-C - //Create our new stream that will utilize that connection - self.publish = [[R5Stream alloc] initWithConnection:connection]; - - //Setup our listener to handle events from this stream - self.publish.delegate = self; - -``` - -[PublishExample.m #40](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Publish/PublishExample.m#L40) - - -The `R5StreamDelegate` that is assigned to the `R5Stream` will receive status events for that stream, including connecting, disconnecting, and errors. - -####Attach a Video Source -The R5Stream will need a video and/or audio source to stream from. To attach a video source, you will need to create an `R5Camera` with the `AVCaptureDevice` you wish to stream from. - -```Objective-C - //Get a list of available cameras for this device - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - - //Use the last device (front facing) - AVCaptureDevice *videoDev = [devices lastObject]; - - //Create an R5Camera with that device and specify the max bitrate to allow - //Note : This bitrate will not be respected if it is lower than the encoder can go! - R5Camera *camera = [[R5Camera alloc] initWithDevice:videoDev andBitRate:512]; - - //Set up the resolution we want this camera to use. This can only be set before publishing begins - camera.width = 640; - camera.height = 480; - - //Setup the rotation of the video stream. This is meta data, and is used by the client to rotate the video. No rotation is done on the publisher. - camera.orientation = 90; - - //Add the camera to the stream - [self.publish attachVideo:camera]; -``` - -[PublishExample.m #47](PublishExample.m#L47) - - -`R5Camera.width` and `R5Camera.height` specify the encoded video size to be streamed. `R5Camera` will choose the video format that is closest to this resolution from the camera. - -`R5Camera.orientation` provides meta information to the stream for presentation on the client. The video is not rotated by the device. A value of **90** will provide portrait orientation on receiving devices. - -####Attach an Audio Source -To add audio to a stream a `R5Microphone` object can be attached. It behaves similarly to `R5Camera`, but requires `R5Stream.attachAudio` instead. - -```Objective-C - //Setup a new R5Microphone for streaming audio with that device - R5Microphone *microphone = [[R5Microphone new] initWithDevice:audioDevice]; - microphone.bitrate = 32; - - //Attach the microphone to the stream - [self.publish attachAudio:microphone]; - -``` - -[PublishExample.m #70](PublishExample.m#L70) - - -#### Preview the Publisher -The `R5VideoViewController` will present publishing streams as well as subscribed streams. To preview a publishing stream, it simply needs to attach the `R5Stream`. - -***This is not required to publish - but allows for previewing the stream.*** - -A `R5VideoViewController` can be set on any UIViewController, or created programmatically - -```Objective-C - -(R5VideoViewController *) getNewViewController: (CGRect) frame{ - UIView *view = [[UIView alloc] initWithFrame: frame]; - R5VideoViewController *viewController = [[R5VideoViewController alloc] init]; - viewController.view = view; - return viewController; - } -``` - -[BaseExample.m #101](BaseExample.m#L101) - - -To view the preview before publishing as started, use `R5VideoViewController.showPreview`. - -```Objective-C - [self addChildViewController:self.r5View]; - [self.view addSubview:self.r5View.view]; - - //show the camera before we start! - [self.r5View showPreview:YES]; - - //show the debug information for the stream - [self.r5View showDebugInfo:YES]; -``` - -[BaseExample.m #91](BaseExample.m#L91) - - -Lastly, we attach the Stream to the R5VideoView to see the streaming content. - -```Objective-C -//attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.publish]; -``` - -[BaseExample.m #59](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m#L59) - - -####Start Publishing -The `R5Stream.publish` method will establish the server connection and begin publishing. - -```Objective-C - //start publishing! - [self.publish publish:[self getStreamName:PUBLISH] type:R5RecordTypeLive]; -``` - -[PublishExample.m #79](PublishExample.m#L79) - - -The *type* parameter tells the server the recording mode to use on the server. - -- **R5RecordTypeLive** - Stream but do not record -- **R5RecordTypeRecord** - Stream and record the file name. Replace existing save. -- **R5RecordTypeAppend** - Stream and append the recording to any existing save. - -####View your stream -Open a browser window and navigate to http://your_red5_pro_server_ip:5080//live/streams.jsp to see a list of active streams. Click on the _flash version to subscribe to your stream. \ No newline at end of file diff --git a/Red5ProStreaming/Examples/StreamImage/Readme.md b/Red5ProStreaming/Examples/StreamImage/Readme.md deleted file mode 100644 index 8062160..0000000 --- a/Red5ProStreaming/Examples/StreamImage/Readme.md +++ /dev/null @@ -1,27 +0,0 @@ -#Stream Image Capture - -`R5Stream.getStreamImage` allows the user to capture a screenshot of the stream at any time. - -###Example Code -- ***[StreamImageExample.m](StreamImageExample.m)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - -##Running the example -Begin by publishing to **stream1** from a second device. **stream1** is the default stream1 name that is used by this example. Select the **StreamImage** option to open a subscriber view. - -Touch the screen at any time while streaming to popup a temporary overlay containing the UIImage that is returned from the Red5 Pro SDK. - -##Using getStreamImage -`R5Stream.getStreamImage` returns a UIImage containing a screenshot of the current stream. The image dimensions match the incoming stream dimensions, and contain RGB data. Once streaming, simply call: - -``` -//Get a UI Image with the current frame -UIImage *img = [self.subscribe getStreamImage]; -``` - -[StreamImageExample.m #41](/StreamImageExample.m#L41) - - -The UIImage can be saved to disk, displayed with a UIImageView, or processed in any way that is needed. \ No newline at end of file diff --git a/Red5ProStreaming/Examples/StreamImage/StreamImageExample.h b/Red5ProStreaming/Examples/StreamImage/StreamImageExample.h deleted file mode 100644 index ef95d8d..0000000 --- a/Red5ProStreaming/Examples/StreamImage/StreamImageExample.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// StreamImageExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 11/16/15. -// Copyright © 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" - -@interface StreamImageExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/StreamImage/StreamImageExample.m b/Red5ProStreaming/Examples/StreamImage/StreamImageExample.m deleted file mode 100644 index 6d61351..0000000 --- a/Red5ProStreaming/Examples/StreamImage/StreamImageExample.m +++ /dev/null @@ -1,60 +0,0 @@ -// -// StreamImageExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 11/16/15. -// Copyright © 2015 Infrared5. All rights reserved. -// - -#import "StreamImageExample.h" - -@interface StreamImageExample () - -@end - -@implementation StreamImageExample - --(void)viewDidAppear:(BOOL)animated{ - - - //setup the publish routine - self.subscribe = [self getNewStream:SUBSCRIBE]; - - - //setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - //attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.subscribe]; - - //start subscribing!! - [self.subscribe play:[self getStreamName:SUBSCRIBE] ]; - - -} - --(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ - - if(self.subscribe != nil){ - - //Get a UI Image with the current frame - UIImage *img = [self.subscribe getStreamImage]; - UIImageView *imgView = [[UIImageView alloc] initWithImage:img]; - [self.view addSubview:imgView]; - - - - //Release the image from the view after 4 seconds - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - [imgView removeFromSuperview]; - - - }); - - - } -} - - -@end diff --git a/Red5ProStreaming/Examples/StreamSend/README.md b/Red5ProStreaming/Examples/StreamSend/README.md deleted file mode 100644 index 5f1760a..0000000 --- a/Red5ProStreaming/Examples/StreamSend/README.md +++ /dev/null @@ -1,71 +0,0 @@ -#Auto Reconnection and Events - -`R5Stream.send` allows the publisher to send messages to the server to be sent to all subscribers. - - -###Example Code -- ***[StreamSendExample.m](StreamSendExample.m)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - -##Running the example -Two devices are required to run this example. One as a publisher, and the other as a subscriber. - -Connect the first device (publisher) and make sure the Toggle **Swap Names** is *NOT* selected. Select the **StreamSend** option to begin publishing. - -Connect the second device (subscriber) and toggle the **Swap Names** to on. This will let the example match the name of the publisher application, and notify the example that this is the subscriber. - -Touch the **Send** button on the publisher screen to display a toast with the message value on the subscriber. - - -##Using the R5Stream send -Once the stream has connected you are able to dispatch messages to any connected subscribers. Sending the message is a simple call: - -```Objective-C - [self.publish send:@"onStreamSend" withParam:@"value=A simple string"]; - -``` - -[StreamSendExample.m #74](StreamSendExample.m#L74) - - -###Send Message Format -The publisher send has a specific parameter format that must be observed. A single string variable is able to be sent, and contains a map of all key-value pairs sepereated by a semi-colon. - -```Objective-C -@"key1=value1;key2=value2;key3=value3;" -``` -Not using this format can result in parsing failure on the server and messages will not be dispatched. - -##Receiving R5Stream send calls -In order to handle `R5Stream.send` calls from the publisher, the `R5Stream.client` delegate must be set. This delegate will receive all `R5Stream.send` messages via appropriately named methods. - -```Objective-C -self.subscribe.client = self; -``` - -[StreamSendExample.m #61](StreamSendExample.m#L61) - - -Because the publisher will be sending **onStreamSend**, the subscriber client delegate will need a matching method signature. All methods receive a single string argument containing the variable map provided by the publisher. This map can easily be parsed. - -``` --(void)onStreamSend:(NSString*)value{ - - //get all key value pairs split - NSArray *pairs = [value componentsSeparatedByString:@";"]; - for(int i=0;i -[StreamSendExample.m #94](StreamSendExample.m#L94) - diff --git a/Red5ProStreaming/Examples/StreamSend/StreamSendExample.h b/Red5ProStreaming/Examples/StreamSend/StreamSendExample.h deleted file mode 100644 index e3e7096..0000000 --- a/Red5ProStreaming/Examples/StreamSend/StreamSendExample.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// RPCExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/19/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" -#import "ALToastView.h" - -@interface StreamSendExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/StreamSend/StreamSendExample.m b/Red5ProStreaming/Examples/StreamSend/StreamSendExample.m deleted file mode 100644 index 4d63367..0000000 --- a/Red5ProStreaming/Examples/StreamSend/StreamSendExample.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// RPCExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/19/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "StreamSendExample.h" - - -@interface StreamSendExample () - -@end - -@implementation StreamSendExample - --(void)viewDidAppear:(BOOL)animated{ - - - if([BaseExample getSwapped] == NO){ - - //WE ARE THE PUBLISHER! - - //setup the publish routine - self.publish = [self getNewStream:PUBLISH]; - - [self setupDefaultR5ViewController]; - - [self.r5View attachStream:self.publish]; - - //set this class to handle RPC response - self.publish.client = self; - - [self.publish publish:[self getStreamName:PUBLISH] type:R5RecordTypeLive]; - - - //Button to send RPC - UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [button addTarget:self - action:@selector(sendMessage:) - forControlEvents:UIControlEventTouchUpInside]; - [button setTitle:@"Send" forState:UIControlStateNormal]; - button.backgroundColor = [UIColor whiteColor]; - button.titleLabel.textColor = [UIColor blackColor]; - - button.frame = CGRectMake(10.0, 310.0, 160.0, 40.0); - [self.view addSubview:button]; - - - }else{ - - //subscribe to the publisher app! - - self.subscribe = [self getNewStream:SUBSCRIBE]; - - [self setupDefaultR5ViewController]; - - [self.r5View attachStream:self.subscribe]; - - self.subscribe.client = self; - - [self.subscribe play:[self getStreamName:SUBSCRIBE]]; - - } - -} - --(void)sendMessage:(id)sender{ - NSLog(@"Sending message!"); - - //R5Stream send requires parameter to be a mapped key value pairs seperated with ';' - //key1=value1;key2=value2; ... - [self.publish send:@"onStreamSend" withParam:@"value=A simple string"]; - - -} - --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - //if we error out - reconnect! - if(stream == self.publish && statusCode == r5_status_start_streaming){ - NSLog(@"Publish stream ready to send RPC"); - } - - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; -} - -/** - * R5Stream.client handler - * - * @param value KeyValue map from publisher - */ --(void)onStreamSend:(NSString*)value{ - - //get all key value pairs split - NSArray *pairs = [value componentsSeparatedByString:@";"]; - for(int i=0;i -[SubscribeExample.m #29](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m#L29) - - -####Setup R5Stream -The `R5Stream` handles both subscribing and publishing. Creating one simply requires the connection already created. - -``` -Objective-C - //Create our new stream that will utilize that connection - self.subscribe = [[R5Stream alloc] initWithConnection:connection]; - - //Setup our listener to handle events from this stream self.subscribe.delegate = self; - ``` - - -[SubscribeExample.m #46](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m#L46) - - -The `R5StreamDelegate` that is assigned to the `R5Stream` will receive status events for that stream, including connecting, disconnecting, and errors. - - -#### Preview the Subscriber -The `R5VideoViewController` will present publishing streams as well as subscribed streams. To view the subscribing stream, it simply needs to attach the `R5Stream`. - ->For a more complete example of the `R5VideoViewController`, view the [PublishExample](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Publish). For this example we will simply utilize `BaseExample.setupDefaultR5ViewController` - - -####Start Subscribing -The `R5Stream.Subscribe` method will establish the server connection and begin Subscribing. - -``` -Objective-C - //start subscribing!! - [self.subscribe play:[self getStreamName:SUBSCRIBE] ]; - -``` - -[SubscribeExample.m #57](https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m#L57) - diff --git a/Red5ProStreaming/Examples/Subscribe/SubscribeExample.h b/Red5ProStreaming/Examples/Subscribe/SubscribeExample.h deleted file mode 100644 index 1c1138f..0000000 --- a/Red5ProStreaming/Examples/Subscribe/SubscribeExample.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// SubscribeExample.h -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "BaseExample.h" - -@interface SubscribeExample : BaseExample - -@end diff --git a/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m b/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m deleted file mode 100644 index 30593f1..0000000 --- a/Red5ProStreaming/Examples/Subscribe/SubscribeExample.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// SubscribeExample.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/17/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "SubscribeExample.h" - -@interface SubscribeExample () - -@end - -@implementation SubscribeExample - --(void)viewDidAppear:(BOOL)animated{ - - //set up the subscriber - short method - //self.subscribe = [self getNewStream:SUBSCRIBE]; - - - /* - * - Step by step - * - */ - - //Get our connection settings - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - //Setup a configuration object for our connection - R5Configuration *config = [[R5Configuration alloc] init]; - config.host = [dict objectForKey:@"domain"]; - config.contextName = [dict objectForKey:@"context"]; - config.port = [(NSNumber *)[dict objectForKey:@"port"] intValue]; - config.protocol = 1; - - //How long to stream the buffer before beginning playback - config.buffer_time = 1; - - //Create a new connection using the configuration above - R5Connection *connection = [[R5Connection alloc] initWithConfig: config]; - - //Create our new stream that will utilize that connection - self.subscribe = [[R5Stream alloc] initWithConnection:connection]; - - //Setup our listener to handle events from this stream - self.subscribe.delegate = self; - - //setup our R5VideoViewController to display the stream content - [self setupDefaultR5ViewController]; - - //attach the R5VideoViewController to our publishing stream - [self.r5View attachStream:self.subscribe]; - - //start subscribing!! - [self.subscribe play:[self getStreamName:SUBSCRIBE] ]; - - -} - -/** - * Handle self.subscribe.delegate callbacks - * - * @param stream stream making callback - * @param statusCode code of message - * @param msg addtional context - */ --(void)onR5StreamStatus:(R5Stream *)stream withStatus:(int)statusCode withMessage:(NSString *)msg{ - - //pass to super to present toast - [super onR5StreamStatus:stream withStatus:statusCode withMessage:msg]; -} - -@end diff --git a/Red5ProStreaming/Examples/SwiftPublish/Readme.md b/Red5ProStreaming/Examples/SwiftPublish/Readme.md deleted file mode 100644 index e92a55a..0000000 --- a/Red5ProStreaming/Examples/SwiftPublish/Readme.md +++ /dev/null @@ -1,8 +0,0 @@ -#Swift Publish Example - -This example shows how to use Swift to publish through Red5 Pro. For details on the properties, and how to properly use R5Stream, view the [PublishExample](../Publish). - -###Example Code -- ***[SwiftPublishExample.swift](SwiftPublishExample.swift)*** - - diff --git a/Red5ProStreaming/Examples/SwiftPublish/SwiftPublishExample.swift b/Red5ProStreaming/Examples/SwiftPublish/SwiftPublishExample.swift deleted file mode 100644 index 3e9c10f..0000000 --- a/Red5ProStreaming/Examples/SwiftPublish/SwiftPublishExample.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// SwiftPublishExample.swift -// Red5ProStreaming -// -// Created by Andy Zupko on 11/23/15. -// Copyright © 2015 Infrared5. All rights reserved. -// - -import Foundation -import UIKit -import AVFoundation -import R5Streaming - -@objc class PublishSwiftViewController: BaseExample { - - - override func viewDidAppear(animated: Bool) { - - super.viewDidAppear(animated) - - AVAudioSession.sharedInstance().requestRecordPermission { (gotPerm: Bool) -> Void in - NSLog("got permission"); - }; - - r5_set_log_level((Int32)(r5_log_level_debug.rawValue)) - - let path = NSBundle.mainBundle().pathForResource("connection", ofType: "plist") - - let dict : NSDictionary? = NSDictionary(contentsOfFile: path!) - - //Setup a configuration object for our connection - let config = R5Configuration() - config.host = dict!["domain"] as? String - config.contextName = dict!["context"] as? String - config.port = (dict!["port"] as? NSNumber)!.intValue - config.`protocol` = 1; - config.buffer_time = 1; - - // Set up the connection and stream - let connection = R5Connection(config: config) - publish = R5Stream(connection: connection) - publish.delegate = self - - //Attach video to stream - let videoDevice = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo).last as? AVCaptureDevice - - let camera = R5Camera(device: videoDevice, andBitRate: 512) - camera.width = 640 - camera.height = 480 - camera.orientation = 90 - publish.attachVideo(camera) - - // Attach the audio from microphone to stream - let audioDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio) - let microphone = R5Microphone(device: audioDevice) - microphone.bitrate = 32 - - publish.attachAudio(microphone) - - //Setup the r5View from BaseExample - setupDefaultR5ViewController() - - //attach the stream! - r5View.attachStream(publish) - - // Start streaming - publish.publish(getStreamName(PUBLISH), type: R5RecordTypeLive) - - } - - - override func onR5StreamStatus(stream: R5Stream!, withStatus statusCode: Int32, withMessage msg: String!) { - print("StatusCode = \(statusCode) with message \"\(msg)\"") - } -} - diff --git a/Red5ProStreaming/Examples/TwoWayVideoChat/Readme.md b/Red5ProStreaming/Examples/TwoWayVideoChat/Readme.md deleted file mode 100644 index aa0fb1e..0000000 --- a/Red5ProStreaming/Examples/TwoWayVideoChat/Readme.md +++ /dev/null @@ -1,90 +0,0 @@ -#Two Way Video Chat - -This example demonstrates two way communication using Red5 Pro. It also demonstrates using Remote Procedure Calls (RPC) on the server. - -###Example Code -- ***[TwoWayVideoChatExample.m](TwoWayVideoChatExample.m)*** - -- ***[BaseExample.m]( -https://github.com/red5pro/streaming-ios/blob/master/Red5ProStreaming/BaseExample.m)*** - -###Setup -Two way communication simply requires setting up a publish stream and a subscribe stream at the same time. You can test the example with two devices. On the second device select the **Swap Names** toggle on the main screen before launching. - -Once the streams have populated the table you can choose the other device by its name. - -###Getting Live Streams -You can make RPC calls to the server using `R5Connection.call`. The call is similar to `R5Stream.send` but allows you to specify a return method name. - -`streams.getLiveStreams` is a built in RPC in all Red5 Pro servers. This call will return a string value that contains a json array of all streams that are currently publishing. - -```Objective-C - //call out to get our new stream - [self.publish.connection call:@"streams.getLiveStreams" withReturn:@"onGetLiveStreams" withParam:nil]; -``` - -The return method will be called on the `R5Stream.client`. The client will need a method that matches the signature of the return. Since `streams.getLiveStreams` returns a string, a void method with a single string parameter will handle the result. - -```Objective-C --(void)onGetLiveStreams:(NSString *)streams{ - - NSError *e = nil; - - self.streams = [NSJSONSerialization JSONObjectWithData: [streams dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingMutableContainers error: &e]; - - //set this as our table data source - - [self.tableView reloadData]; - - if(self.subscribe == nil){ - - self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(getStreams:) userInfo:nil repeats:NO]; - } - - -} -``` -A simple json parsing will get all streams, and start the timer over to request the streams again. The table view is loaded with the new stream names on the return to display for subscription. - - -###Detecting Video Loss -A common feature of two way video chat is handling when video does not have enough bandwidth to broadcast properly. This example displays a profile icon over the subscription stream when this is detected. You can force this to happen by setting **showVideo** to NO in connection.plist on your other device. - -In order to detect if video is currently being streamed, the `R5Stream.client` can implement `-(void)onR5PublishStateNotification:(NSString*)value;` -This method returns a mapped value of properties related to the publishing stream - and is called by the remote publisher on a fixed interval. - -One of the keys in the state notification is `streamingMode`. This example shows that if **"Video"** is not found in the streamMode, display the profile icon until it is broadcasting video again. - -```Objective-C --(void)onR5PublishStateNotification:(NSString*)value{ - - NSArray *pairs = [value componentsSeparatedByString:@";"]; - - for(int i=0;i -@property NSArray *streams; -@property UITableView *tableView; -@property NSTimer *timer; -@property R5VideoViewController *subscribeR5View; -@property UIView *profileView; -@end - -@implementation TwoWayVideoChatExample - - - - --(void)viewDidAppear:(BOOL)animated{ - - - //add a table view to display list of streams - self.tableView = [[UITableView alloc] initWithFrame:self.view.frame]; - self.tableView.delegate = self; - self.tableView.dataSource = self; - - CGRect rect = self.tableView.frame; - CGFloat topBarOffset = self.topLayoutGuide.length; - - rect.origin.y = topBarOffset ; - rect.size.height = rect.size.height - (topBarOffset); - - self.tableView.frame = rect; - - [self.view addSubview:self.tableView]; - - //setup the publish routine - self.publish = [self getNewStream:PUBLISH]; - [self setupDefaultR5ViewController]; - - self.r5View.view.frame = CGRectMake(self.view.frame.size.width-90, self.view.frame.size.height-150, 80, 140); - - [self.r5View attachStream:self.publish]; - //set this class to handle RPC response - self.publish.client = self; - - [self.publish publish:[self getStreamName:PUBLISH] type:R5RecordTypeLive]; - - -} - --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - if(self.streams != nil){ - - return self.streams.count; - } - return 0; -} - - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *CellIdentifier = @"streamCell"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"streamCell"]; - - if (cell == nil) { - cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; - } - - cell.textLabel.text = [self.streams objectAtIndex:indexPath.item]; - - return cell; -} - --(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - NSString *streamname = [self.streams objectAtIndex:indexPath.item]; - - self.subscribe = [self getNewStream:SUBSCRIBE]; - self.subscribe.client = self; - - R5VideoViewController *rvc = [self getNewViewController:self.view.frame]; - [rvc attachStream: self.subscribe]; - - [self.view addSubview:rvc.view]; - - //bring publish to the front - [self.view bringSubviewToFront:self.r5View.view]; - - [self.subscribe play:streamname]; - - self.subscribeR5View = rvc; - - [self addProfileOverlay]; - - -} - --(void)addProfileOverlay{ - - self.profileView = [[UIView alloc] initWithFrame:self.view.frame]; - self.profileView.backgroundColor = [UIColor blackColor]; - - UIImage *vid_icon = [UIImage imageNamed:@"video_icon"]; - - UIImageView *imgView = [[UIImageView alloc] initWithImage:vid_icon]; - - CGRect frame = self.profileView.frame; - frame.size.height *= 0.5; - frame.size.width *= 0.5; - frame.origin.x = (self.profileView.frame.size.width-frame.size.width)*0.5; - frame.origin.y = (self.profileView.frame.size.height-frame.size.height)*0.5; - - imgView.frame = frame; - - imgView.contentMode = UIViewContentModeScaleAspectFit; - - [self.profileView addSubview:imgView]; - self.profileView.hidden = YES; - - [self.subscribeR5View.view addSubview:self.profileView]; - -} - - --(void)onR5PublishStateNotification:(NSString*)value{ - - NSArray *pairs = [value componentsSeparatedByString:@";"]; - - for(int i=0;i - - -@interface ViewController : UIViewController - -@end - diff --git a/Red5ProStreaming/ViewController.m b/Red5ProStreaming/ViewController.m deleted file mode 100644 index 87c8796..0000000 --- a/Red5ProStreaming/ViewController.m +++ /dev/null @@ -1,126 +0,0 @@ -// -// ViewController.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import "ViewController.h" -#import "AdaptiveBitrateExample.h" -#import "PublishExample.h" -#import "SubscribeExample.h" -#import "AutoReconnectExample.h" -#import -#import "TwoWayVideoChatExample.h" -#import "StreamSendExample.h" -#import "StreamImageExample.h" -#import "CustomVideoSourceExample.h" -#import "Red5ProStreaming-Swift.h" -#import "ClusteringExample.h" - -@interface ViewController () - -@end - -@implementation ViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - - r5_set_log_level(r5_log_level_debug); - - - [self setEdgesForExtendedLayout:UIRectEdgeNone]; - - - -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -- (IBAction)onSwapNames:(id)sender { - - UISwitch *switcher = (UISwitch*)sender; - - [BaseExample setSwapped:switcher.on]; - -} - --(void) showExample : (UIViewController*)viewController{ - - - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"connection" ofType:@"plist"]]; - - if([[dict objectForKey:@"domain"] isEqualToString:@"0.0.0.0"]){ - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Server!" message:@"Set the domain in your connection.plist!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; - [alert show]; - return; - - } - - UIView *view = [[UIView alloc] initWithFrame:self.view.frame]; - viewController.view = view; - - [self.navigationController pushViewController:viewController animated:YES]; - -} - -- (IBAction)onSwiftPublish:(id)sender { - - [self showExample:(UIViewController*)[PublishSwiftViewController new]]; -} - -- (IBAction)onTwoWay:(id)sender { - - [self showExample:[TwoWayVideoChatExample new]]; -} - -- (IBAction)onReconnect:(id)sender { - [self showExample:[AutoReconnectExample new]]; -} - - -- (IBAction)onAdaptiveBitrate:(id)sender { - - [self showExample:[AdaptiveBitrateExample new]]; - - -} - -- (IBAction)onSubscribe:(id)sender { - - [self showExample:[SubscribeExample new]]; - -} - -- (IBAction)onPublish:(id)sender { - - [self showExample:[PublishExample new]]; -} - -- (IBAction)onRPC:(id)sender { - - [self showExample:[StreamSendExample new]]; -} - -- (IBAction)onStreamImage:(id)sender { - [self showExample:[StreamImageExample new]]; -} - -- (IBAction)onCustomVideoSource:(id)sender { - [self showExample:[CustomVideoSourceExample new]]; -} - -- (IBAction)onClustering:(id)sender { - [self showExample:[ClusteringExample new]]; -} - --(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ - -} - -@end diff --git a/Red5ProStreaming/connection.plist b/Red5ProStreaming/connection.plist deleted file mode 100644 index 813faff..0000000 --- a/Red5ProStreaming/connection.plist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - domain - 0.0.0.0 - context - live - port - 8554 - stream1 - stream1 - stream2 - stream2 - showVideo - - - diff --git a/Red5ProStreaming/main.m b/Red5ProStreaming/main.m deleted file mode 100644 index 7c8290a..0000000 --- a/Red5ProStreaming/main.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// main.m -// Red5ProStreaming -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/Red5ProStreaming/video_icon.png b/Red5ProStreaming/video_icon.png deleted file mode 100644 index f49e931..0000000 Binary files a/Red5ProStreaming/video_icon.png and /dev/null differ diff --git a/Red5ProStreamingTests/Info.plist b/Red5ProStreamingTests/Info.plist deleted file mode 100644 index b4f3caf..0000000 --- a/Red5ProStreamingTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - com.infrared5.$(PRODUCT_NAME:rfc1034identifier) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/Red5ProStreamingTests/Red5ProStreamingTests.m b/Red5ProStreamingTests/Red5ProStreamingTests.m deleted file mode 100644 index 59b7fc2..0000000 --- a/Red5ProStreamingTests/Red5ProStreamingTests.m +++ /dev/null @@ -1,40 +0,0 @@ -// -// Red5ProStreamingTests.m -// Red5ProStreamingTests -// -// Created by Andy Zupko on 6/16/15. -// Copyright (c) 2015 Infrared5. All rights reserved. -// - -#import -#import - -@interface Red5ProStreamingTests : XCTestCase - -@end - -@implementation Red5ProStreamingTests - -- (void)setUp { - [super 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. - [super tearDown]; -} - -- (void)testExample { - // This is an example of a functional test case. - XCTAssert(YES, @"Pass"); -} - -- (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