diff options
author | Joel Fischer <joeljfischer@gmail.com> | 2017-09-06 13:24:27 -0400 |
---|---|---|
committer | Joel Fischer <joeljfischer@gmail.com> | 2017-09-06 13:24:27 -0400 |
commit | 35fb42227aaddce894016e0315fd4c0a6f9df1e7 (patch) | |
tree | 9fe42532cd6751df44b09ba8b0406ab9412ca8af | |
parent | 1c30d20f2fb31bf7b5d229437fcc0eb6f6b378eb (diff) | |
parent | 39b01057a545aa95e8048c941df7c5a8d54744ff (diff) | |
download | sdl_ios-35fb42227aaddce894016e0315fd4c0a6f9df1e7.tar.gz |
Merge branch 'release/5.0.0' into bugfix/issue_636_proxylistener_deprecatedbugfix/issue_636_proxylistener_deprecated
# Conflicts:
# SmartDeviceLink-iOS.xcodeproj/project.pbxproj
# SmartDeviceLinkTests/ProtocolSpecs/SDLFunctionIDSpec.m
60 files changed, 1758 insertions, 41 deletions
diff --git a/SmartDeviceLink-iOS.podspec b/SmartDeviceLink-iOS.podspec index d097938b9..308f5987b 100644 --- a/SmartDeviceLink-iOS.podspec +++ b/SmartDeviceLink-iOS.podspec @@ -104,6 +104,7 @@ ss.public_header_files = [ 'SmartDeviceLink/SDLGetWaypointsResponse.h', 'SmartDeviceLink/SDLGlobalProperty.h', 'SmartDeviceLink/SDLGPSData.h', +'SmartDeviceLink/SDLHapticRect.h', 'SmartDeviceLink/SDLHeadLampStatus.h', 'SmartDeviceLink/SDLHMICapabilities.h', 'SmartDeviceLink/SDLHMILevel.h', @@ -222,6 +223,8 @@ ss.public_header_files = [ 'SmartDeviceLink/SDLScrollableMessage.h', 'SmartDeviceLink/SDLScrollableMessageResponse.h', 'SmartDeviceLink/SDLSecurityType.h', +'SmartDeviceLink/SDLSendHapticData.h', +'SmartDeviceLink/SDLSendHapticDataResponse.h', 'SmartDeviceLink/SDLSendLocation.h', 'SmartDeviceLink/SDLSendLocationResponse.h', 'SmartDeviceLink/SDLSetAppIcon.h', diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj index 16b44eda5..a88890f31 100644 --- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj +++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj @@ -875,6 +875,9 @@ 5DA49CE61F1EA83300E65FC5 /* SDLControlFramePayloadRPCStartService.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA49CE41F1EA83300E65FC5 /* SDLControlFramePayloadRPCStartService.m */; }; 5DA8A0E91E955F710039C50D /* SDLStreamingMediaManagerConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8966F31E56977C00413EAB /* SDLStreamingMediaManagerConstants.m */; }; 5DA8A0EA1E955FE00039C50D /* SDLLogModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DBF06301E64A9C600A5CF03 /* SDLLogModel.m */; }; + 5DADA7751F4DFED60084D17D /* SDLRectangle.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DADA7731F4DFED60084D17D /* SDLRectangle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5DADA7761F4DFED60084D17D /* SDLRectangle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DADA7741F4DFED60084D17D /* SDLRectangle.m */; }; + 5DADA7781F4E059E0084D17D /* SDLRectangleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */; }; 5DAE06731BDEC6C000F9B498 /* SDLFileSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE06721BDEC6C000F9B498 /* SDLFileSpec.m */; }; 5DAE06751BDEC6D600F9B498 /* SDLArtworkSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE06741BDEC6D600F9B498 /* SDLArtworkSpec.m */; }; 5DB1BCD31D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1BCD01D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m */; }; @@ -939,6 +942,15 @@ 5DE5ABB71B0E38C90067BB02 /* SDLSystemRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D61FBD21A84238B00846EE7 /* SDLSystemRequest.h */; }; 5DE5ABB81B0E38C90067BB02 /* SDLSystemRequestResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D61FBD41A84238B00846EE7 /* SDLSystemRequestResponse.h */; }; 5DFFB9151BD7C89700DB3F04 /* SDLConnectionManagerType.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DFFB9141BD7C89700DB3F04 /* SDLConnectionManagerType.h */; }; + 8877F5EB1F34A3BE00DC128A /* SDLSendHapticDataSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8877F5EA1F34A3BE00DC128A /* SDLSendHapticDataSpec.m */; }; + 8877F5EE1F34A72200DC128A /* SDLSendHapticDataResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 8877F5EC1F34A72200DC128A /* SDLSendHapticDataResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8877F5EF1F34A72200DC128A /* SDLSendHapticDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 8877F5ED1F34A72200DC128A /* SDLSendHapticDataResponse.m */; }; + 8877F5F11F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8877F5F01F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m */; }; + 88EED8381F33AE1700E6C42E /* SDLHapticRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EED8361F33AE1700E6C42E /* SDLHapticRect.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88EED8391F33AE1700E6C42E /* SDLHapticRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EED8371F33AE1700E6C42E /* SDLHapticRect.m */; }; + 88EED83B1F33BECB00E6C42E /* SDLHapticRectSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EED83A1F33BECB00E6C42E /* SDLHapticRectSpec.m */; }; + 88EED83E1F33C5A400E6C42E /* SDLSendHapticData.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EED83C1F33C5A400E6C42E /* SDLSendHapticData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 88EED83F1F33C5A400E6C42E /* SDLSendHapticData.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EED83D1F33C5A400E6C42E /* SDLSendHapticData.m */; }; 8B7B319A1F2F7B5700BDC38D /* SDLVideoStreamingCodec.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B7B31981F2F7B5700BDC38D /* SDLVideoStreamingCodec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8B7B319B1F2F7B5700BDC38D /* SDLVideoStreamingCodec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B7B31991F2F7B5700BDC38D /* SDLVideoStreamingCodec.m */; }; 8B7B319E1F2F7CF700BDC38D /* SDLVideoStreamingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B7B319C1F2F7CF700BDC38D /* SDLVideoStreamingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1040,6 +1052,13 @@ E9C32B9D1AB20C5900F283AF /* EAAccessory+SDLProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = E9C32B991AB20C5900F283AF /* EAAccessory+SDLProtocols.m */; }; E9C32B9E1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = E9C32B9A1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.h */; }; E9C32B9F1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = E9C32B9B1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.m */; }; + EED5C9FE1F4D18D100F04000 /* SDLH264Packetizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EED5C9FD1F4D18D100F04000 /* SDLH264Packetizer.h */; }; + EED5CA001F4D18DC00F04000 /* SDLH264ByteStreamPacketizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EED5C9FF1F4D18DC00F04000 /* SDLH264ByteStreamPacketizer.h */; }; + EED5CA021F4D18EC00F04000 /* SDLH264ByteStreamPacketizer.m in Sources */ = {isa = PBXBuildFile; fileRef = EED5CA011F4D18EC00F04000 /* SDLH264ByteStreamPacketizer.m */; }; + EED5CA041F4D1D5E00F04000 /* SDLH264ByteStreamPacketizerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = EED5CA031F4D1D5E00F04000 /* SDLH264ByteStreamPacketizerSpec.m */; }; + EED5CA061F4D1E2300F04000 /* SDLRTPH264Packetizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EED5CA051F4D1E2300F04000 /* SDLRTPH264Packetizer.h */; }; + EED5CA081F4D1E2E00F04000 /* SDLRTPH264Packetizer.m in Sources */ = {isa = PBXBuildFile; fileRef = EED5CA071F4D1E2E00F04000 /* SDLRTPH264Packetizer.m */; }; + EED5CA0A1F4D206800F04000 /* SDLRTPH264PacketizerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = EED5CA091F4D206800F04000 /* SDLRTPH264PacketizerSpec.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1994,6 +2013,9 @@ 5DA3F36F1BC4489A0026F2D0 /* SDLManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLManager.m; sourceTree = "<group>"; }; 5DA49CE31F1EA83300E65FC5 /* SDLControlFramePayloadRPCStartService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLControlFramePayloadRPCStartService.h; sourceTree = "<group>"; }; 5DA49CE41F1EA83300E65FC5 /* SDLControlFramePayloadRPCStartService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLControlFramePayloadRPCStartService.m; sourceTree = "<group>"; }; + 5DADA7731F4DFED60084D17D /* SDLRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLRectangle.h; sourceTree = "<group>"; }; + 5DADA7741F4DFED60084D17D /* SDLRectangle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLRectangle.m; sourceTree = "<group>"; }; + 5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLRectangleSpec.m; sourceTree = "<group>"; }; 5DAE06721BDEC6C000F9B498 /* SDLFileSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLFileSpec.m; path = DevAPISpecs/SDLFileSpec.m; sourceTree = "<group>"; }; 5DAE06741BDEC6D600F9B498 /* SDLArtworkSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLArtworkSpec.m; path = DevAPISpecs/SDLArtworkSpec.m; sourceTree = "<group>"; }; 5DB1BCD01D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDeleteFileOperationSpec.m; path = DevAPISpecs/SDLDeleteFileOperationSpec.m; sourceTree = "<group>"; }; @@ -2062,6 +2084,15 @@ 5DEE55BF1B8509CB004F0D0F /* SDLURLRequestTaskSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLURLRequestTaskSpec.m; path = "UtilitiesSpecs/HTTP Connection/SDLURLRequestTaskSpec.m"; sourceTree = "<group>"; }; 5DF2BB9C1B94E38A00CE5994 /* SDLURLSessionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLURLSessionSpec.m; path = "UtilitiesSpecs/HTTP Connection/SDLURLSessionSpec.m"; sourceTree = "<group>"; }; 5DFFB9141BD7C89700DB3F04 /* SDLConnectionManagerType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLConnectionManagerType.h; sourceTree = "<group>"; }; + 8877F5EA1F34A3BE00DC128A /* SDLSendHapticDataSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSendHapticDataSpec.m; sourceTree = "<group>"; }; + 8877F5EC1F34A72200DC128A /* SDLSendHapticDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLSendHapticDataResponse.h; sourceTree = "<group>"; }; + 8877F5ED1F34A72200DC128A /* SDLSendHapticDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSendHapticDataResponse.m; sourceTree = "<group>"; }; + 8877F5F01F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSendHapticDataResponseSpec.m; sourceTree = "<group>"; }; + 88EED8361F33AE1700E6C42E /* SDLHapticRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLHapticRect.h; sourceTree = "<group>"; }; + 88EED8371F33AE1700E6C42E /* SDLHapticRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLHapticRect.m; sourceTree = "<group>"; }; + 88EED83A1F33BECB00E6C42E /* SDLHapticRectSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLHapticRectSpec.m; sourceTree = "<group>"; }; + 88EED83C1F33C5A400E6C42E /* SDLSendHapticData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLSendHapticData.h; sourceTree = "<group>"; }; + 88EED83D1F33C5A400E6C42E /* SDLSendHapticData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSendHapticData.m; sourceTree = "<group>"; }; 8B7B31981F2F7B5700BDC38D /* SDLVideoStreamingCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLVideoStreamingCodec.h; sourceTree = "<group>"; }; 8B7B31991F2F7B5700BDC38D /* SDLVideoStreamingCodec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLVideoStreamingCodec.m; sourceTree = "<group>"; }; 8B7B319C1F2F7CF700BDC38D /* SDLVideoStreamingProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLVideoStreamingProtocol.h; sourceTree = "<group>"; }; @@ -2165,6 +2196,13 @@ E9C32B991AB20C5900F283AF /* EAAccessory+SDLProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "EAAccessory+SDLProtocols.m"; sourceTree = "<group>"; }; E9C32B9A1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "EAAccessoryManager+SDLProtocols.h"; sourceTree = "<group>"; }; E9C32B9B1AB20C5900F283AF /* EAAccessoryManager+SDLProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "EAAccessoryManager+SDLProtocols.m"; sourceTree = "<group>"; }; + EED5C9FD1F4D18D100F04000 /* SDLH264Packetizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLH264Packetizer.h; sourceTree = "<group>"; }; + EED5C9FF1F4D18DC00F04000 /* SDLH264ByteStreamPacketizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLH264ByteStreamPacketizer.h; sourceTree = "<group>"; }; + EED5CA011F4D18EC00F04000 /* SDLH264ByteStreamPacketizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLH264ByteStreamPacketizer.m; sourceTree = "<group>"; }; + EED5CA031F4D1D5E00F04000 /* SDLH264ByteStreamPacketizerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLH264ByteStreamPacketizerSpec.m; path = DevAPISpecs/SDLH264ByteStreamPacketizerSpec.m; sourceTree = "<group>"; }; + EED5CA051F4D1E2300F04000 /* SDLRTPH264Packetizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLRTPH264Packetizer.h; sourceTree = "<group>"; }; + EED5CA071F4D1E2E00F04000 /* SDLRTPH264Packetizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLRTPH264Packetizer.m; sourceTree = "<group>"; }; + EED5CA091F4D206800F04000 /* SDLRTPH264PacketizerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLRTPH264PacketizerSpec.m; path = DevAPISpecs/SDLRTPH264PacketizerSpec.m; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2382,6 +2420,7 @@ 162E82631A9BDE8A00906325 /* SDLUnsubscribeVehicleDataSpec.m */, DA9F7EA51DCC05F500ACAE48 /* SDLUnsubscribeWaypointsSpec.m */, 162E82641A9BDE8A00906325 /* SDLUpdateTurnListSpec.m */, + 8877F5EA1F34A3BE00DC128A /* SDLSendHapticDataSpec.m */, ); path = RequestSpecs; sourceTree = "<group>"; @@ -2435,6 +2474,7 @@ 162E828C1A9BDE8A00906325 /* SDLUnsubscribeVehicleDataResponseSpec.m */, DA9F7EAB1DCC062400ACAE48 /* SDLUnsubscribeWaypointsResponseSpec.m */, 162E828D1A9BDE8A00906325 /* SDLUpdateTurnListResponseSpec.m */, + 8877F5F01F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m */, ); path = ResponseSpecs; sourceTree = "<group>"; @@ -2458,6 +2498,7 @@ 162E829A1A9BDE8A00906325 /* SDLECallInfoSpec.m */, 162E829B1A9BDE8A00906325 /* SDLEmergencyEventSpec.m */, 162E829C1A9BDE8A00906325 /* SDLGPSDataSpec.m */, + 88EED83A1F33BECB00E6C42E /* SDLHapticRectSpec.m */, 162E829D1A9BDE8A00906325 /* SDLHeadLampStatusSpec.m */, 5DE372A31ACB336600849FAA /* SDLHMICapabilitiesSpec.m */, 162E829E1A9BDE8A00906325 /* SDLHMIPermissionsSpec.m */, @@ -2475,6 +2516,7 @@ 162E82A51A9BDE8A00906325 /* SDLPermissionItemSpec.m */, 5D0A9F941F15585B00CC80DD /* SDLPhoneCapabilitySpec.m */, 162E82A61A9BDE8A00906325 /* SDLPresetBankCapabilitiesSpec.m */, + 5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */, 162E82A71A9BDE8A00906325 /* SDLScreenParamsSpec.m */, 162E82A81A9BDE8A00906325 /* SDLSingleTireStatusSpec.m */, 162E82A91A9BDE8A00906325 /* SDLSoftButtonCapabilitiesSpec.m */, @@ -2915,6 +2957,8 @@ 5D61FB771A84238B00846EE7 /* SDLResetGlobalProperties.m */, 5D61FB8E1A84238B00846EE7 /* SDLScrollableMessage.h */, 5D61FB8F1A84238B00846EE7 /* SDLScrollableMessage.m */, + 88EED83C1F33C5A400E6C42E /* SDLSendHapticData.h */, + 88EED83D1F33C5A400E6C42E /* SDLSendHapticData.m */, 5DCF76F31ACDBAD300BB647B /* SDLSendLocation.h */, 5DCF76F41ACDBAD300BB647B /* SDLSendLocation.m */, 5D61FB921A84238B00846EE7 /* SDLSetAppIcon.h */, @@ -3014,6 +3058,8 @@ 5D61FB791A84238B00846EE7 /* SDLResetGlobalPropertiesResponse.m */, 5D61FB901A84238B00846EE7 /* SDLScrollableMessageResponse.h */, 5D61FB911A84238B00846EE7 /* SDLScrollableMessageResponse.m */, + 8877F5EC1F34A72200DC128A /* SDLSendHapticDataResponse.h */, + 8877F5ED1F34A72200DC128A /* SDLSendHapticDataResponse.m */, 5DCF76F71ACDD7CD00BB647B /* SDLSendLocationResponse.h */, 5DCF76F81ACDD7CD00BB647B /* SDLSendLocationResponse.m */, 5D61FB941A84238B00846EE7 /* SDLSetAppIconResponse.h */, @@ -3091,6 +3137,8 @@ 5D61FAAE1A84238A00846EE7 /* SDLEmergencyEvent.m */, 5D61FACE1A84238A00846EE7 /* SDLGPSData.h */, 5D61FACF1A84238A00846EE7 /* SDLGPSData.m */, + 88EED8361F33AE1700E6C42E /* SDLHapticRect.h */, + 88EED8371F33AE1700E6C42E /* SDLHapticRect.m */, 5D61FAD01A84238A00846EE7 /* SDLHeadLampStatus.h */, 5D61FAD11A84238A00846EE7 /* SDLHeadLampStatus.m */, 5DE3729F1ACB2ED300849FAA /* SDLHMICapabilities.h */, @@ -3127,6 +3175,8 @@ 5D00AC7A1F15287E004000D9 /* SDLPhoneCapability.m */, 5D61FB4E1A84238B00846EE7 /* SDLPresetBankCapabilities.h */, 5D61FB4F1A84238B00846EE7 /* SDLPresetBankCapabilities.m */, + 5DADA7731F4DFED60084D17D /* SDLRectangle.h */, + 5DADA7741F4DFED60084D17D /* SDLRectangle.m */, 5D61FB8C1A84238B00846EE7 /* SDLScreenParams.h */, 5D61FB8D1A84238B00846EE7 /* SDLScreenParams.m */, 5D61FBAA1A84238B00846EE7 /* SDLSingleTireStatus.h */, @@ -4150,6 +4200,8 @@ isa = PBXGroup; children = ( DA8966EE1E5693E300413EAB /* SDLStreamingMediaLifecycleManagerSpec.m */, + EED5CA031F4D1D5E00F04000 /* SDLH264ByteStreamPacketizerSpec.m */, + EED5CA091F4D206800F04000 /* SDLRTPH264PacketizerSpec.m */, ); name = Streaming; sourceTree = "<group>"; @@ -4171,6 +4223,11 @@ DAA41D531DF66B2000BC7337 /* SDLVideoEncoder.h */, DAA41D541DF66B2000BC7337 /* SDLVideoEncoder.m */, 5DA23FFC1F312DBA009C0313 /* SDLVideoEncoderDelegate.h */, + EED5C9FD1F4D18D100F04000 /* SDLH264Packetizer.h */, + EED5C9FF1F4D18DC00F04000 /* SDLH264ByteStreamPacketizer.h */, + EED5CA011F4D18EC00F04000 /* SDLH264ByteStreamPacketizer.m */, + EED5CA051F4D1E2300F04000 /* SDLRTPH264Packetizer.h */, + EED5CA071F4D1E2E00F04000 /* SDLRTPH264Packetizer.m */, ); name = "Video Encoding"; sourceTree = "<group>"; @@ -4307,6 +4364,7 @@ 5D61FD811A84238C00846EE7 /* SDLSetAppIconResponse.h in Headers */, 5D61FC551A84238C00846EE7 /* SDLButtonName.h in Headers */, 5D616B531D59044400553F6B /* SDLErrorConstants.h in Headers */, + 5DADA7751F4DFED60084D17D /* SDLRectangle.h in Headers */, 5D61FD9F1A84238C00846EE7 /* SDLSoftButton.h in Headers */, 5DD67CBC1E661C84009CD394 /* SDLLogTargetOSLog.h in Headers */, 5D61FD551A84238C00846EE7 /* SDLPutFile.h in Headers */, @@ -4354,7 +4412,9 @@ 5D61FC761A84238C00846EE7 /* SDLDeleteFile.h in Headers */, 5D61FD211A84238C00846EE7 /* SDLOnVehicleData.h in Headers */, DA9F7E731DCC004C00ACAE48 /* SDLGetWayPointsResponse.h in Headers */, + 88EED8381F33AE1700E6C42E /* SDLHapticRect.h in Headers */, 5DD67CC31E68AE82009CD394 /* SDLLogFileModuleMap.h in Headers */, + EED5C9FE1F4D18D100F04000 /* SDLH264Packetizer.h in Headers */, 5D61FD7D1A84238C00846EE7 /* SDLScrollableMessageResponse.h in Headers */, 5D61FD431A84238C00846EE7 /* SDLProtocol.h in Headers */, 5D8B17531AC9E11B006A6E1C /* SDLDialNumberResponse.h in Headers */, @@ -4441,6 +4501,7 @@ 5D4D67B01D2FE2F900468B4A /* SDLResponseDispatcher.h in Headers */, 5D61FCF21A84238C00846EE7 /* SDLLockScreenStatus.h in Headers */, 5D00AC771F15283E004000D9 /* SDLNavigationCapability.h in Headers */, + 88EED83E1F33C5A400E6C42E /* SDLSendHapticData.h in Headers */, 5D61FCD31A84238C00846EE7 /* SDLImageResolution.h in Headers */, 8B7B319E1F2F7CF700BDC38D /* SDLVideoStreamingProtocol.h in Headers */, 5D61FD541A84238C00846EE7 /* SDLProxyListener.h in Headers */, @@ -4466,6 +4527,7 @@ 5D61FC721A84238C00846EE7 /* SDLDeleteCommand.h in Headers */, 5D61FC821A84238C00846EE7 /* SDLDeviceInfo.h in Headers */, 5D61FCA91A84238C00846EE7 /* SDLFileType.h in Headers */, + 8877F5EE1F34A72200DC128A /* SDLSendHapticDataResponse.h in Headers */, DA8966F21E56973700413EAB /* SDLStreamingMediaManagerConstants.h in Headers */, 5DD67CB81E661C4A009CD394 /* SDLLogTargetFile.h in Headers */, 5D61FD591A84238C00846EE7 /* SDLReadDID.h in Headers */, @@ -4491,6 +4553,7 @@ 5D61FCE41A84238C00846EE7 /* SDLKeyboardProperties.h in Headers */, 5D61FDED1A84238C00846EE7 /* SDLUnsubscribeVehicleDataResponse.h in Headers */, 5D4631041F2120A30092EFDC /* SDLControlFramePayloadType.h in Headers */, + EED5CA061F4D1E2300F04000 /* SDLRTPH264Packetizer.h in Headers */, 5D61FCCF1A84238C00846EE7 /* SDLImageField.h in Headers */, 5D535DC51B72473800CF7760 /* SDLGlobals.h in Headers */, 5D79A03B1CE36F030035797B /* SDLUploadFileOperation.h in Headers */, @@ -4529,6 +4592,7 @@ 5D61FC5B1A84238C00846EE7 /* SDLChangeRegistration.h in Headers */, 5D61FD5B1A84238C00846EE7 /* SDLReadDIDResponse.h in Headers */, 5D61FDEF1A84238C00846EE7 /* SDLUpdateMode.h in Headers */, + EED5CA001F4D18DC00F04000 /* SDLH264ByteStreamPacketizer.h in Headers */, DA8966EB1E56939F00413EAB /* SDLStreamingMediaLifecycleManager.h in Headers */, 5D61FDDB1A84238C00846EE7 /* SDLTriggerSource.h in Headers */, 5D61FD8F1A84238C00846EE7 /* SDLShow.h in Headers */, @@ -5030,6 +5094,8 @@ 5D1665C91CF8CA3D00CC4CA1 /* SDLPermissionFilter.m in Sources */, 5D61FDBA1A84238C00846EE7 /* SDLSyncPDataResponse.m in Sources */, 5D61FDDC1A84238C00846EE7 /* SDLTriggerSource.m in Sources */, + 5DADA7761F4DFED60084D17D /* SDLRectangle.m in Sources */, + 88EED83F1F33C5A400E6C42E /* SDLSendHapticData.m in Sources */, 5D16545B1D3E7A1600554D93 /* SDLLifecycleManager.m in Sources */, E9C32B971AB20BA200F283AF /* SDLTimer.m in Sources */, 5D61FCB61A84238C00846EE7 /* SDLGetVehicleData.m in Sources */, @@ -5038,6 +5104,7 @@ 5D61FCA41A84238C00846EE7 /* SDLEndAudioPassThru.m in Sources */, 5D8B17541AC9E11B006A6E1C /* SDLDialNumberResponse.m in Sources */, DA6223BE1E7B088200878689 /* CVPixelBufferRef+SDLUtil.m in Sources */, + EED5CA021F4D18EC00F04000 /* SDLH264ByteStreamPacketizer.m in Sources */, 5D61FC851A84238C00846EE7 /* SDLDeviceLevelStatus.m in Sources */, 5D9FDA981F2A7D3F00A495C8 /* emhashmap.c in Sources */, 5D61FD1E1A84238C00846EE7 /* SDLOnTBTClientState.m in Sources */, @@ -5081,6 +5148,7 @@ 5DA3F3551BC448060026F2D0 /* NSMapTable+Subscripting.m in Sources */, 5D61FCD61A84238C00846EE7 /* SDLImageType.m in Sources */, 5D4D67AD1D2ED37A00468B4A /* SDLNotificationDispatcher.m in Sources */, + 88EED8391F33AE1700E6C42E /* SDLHapticRect.m in Sources */, DA9F7E9C1DCC052C00ACAE48 /* SDLLocationDetails.m in Sources */, 5D61FD7A1A84238C00846EE7 /* SDLScreenParams.m in Sources */, 5D61FC831A84238C00846EE7 /* SDLDeviceInfo.m in Sources */, @@ -5089,6 +5157,7 @@ 97E26DED1E807AD70074A3C7 /* SDLMutableDataQueue.m in Sources */, 5D61FD641A84238C00846EE7 /* SDLResetGlobalProperties.m in Sources */, 5D60088B1BE3ED540094A505 /* SDLStateMachine.m in Sources */, + 8877F5EF1F34A72200DC128A /* SDLSendHapticDataResponse.m in Sources */, 5D61FD181A84238C00846EE7 /* SDLOnPermissionsChange.m in Sources */, 5D61FD3E1A84238C00846EE7 /* SDLPrimaryAudioSource.m in Sources */, 5D61FC2A1A84238C00846EE7 /* SDLAbstractProtocol.m in Sources */, @@ -5120,6 +5189,7 @@ 5D61FC2C1A84238C00846EE7 /* SDLAbstractTransport.m in Sources */, 5D61FD8E1A84238C00846EE7 /* SDLSetMediaClockTimerResponse.m in Sources */, 5D61FD721A84238C00846EE7 /* SDLRPCRequest.m in Sources */, + EED5CA081F4D1E2E00F04000 /* SDLRTPH264Packetizer.m in Sources */, 5D61FDF01A84238C00846EE7 /* SDLUpdateMode.m in Sources */, 5D61FC931A84238C00846EE7 /* SDLDisplayType.m in Sources */, 5D61FCE31A84238C00846EE7 /* SDLKeyboardLayout.m in Sources */, @@ -5167,6 +5237,7 @@ 162E837D1A9BDE8B00906325 /* SDLEmergencyEventSpec.m in Sources */, 162E82D31A9BDE8A00906325 /* SDLCarModeStatusSpec.m in Sources */, 8B7B31A91F2FB8BC00BDC38D /* SDLVideoStreamingProtocolSpec.m in Sources */, + 88EED83B1F33BECB00E6C42E /* SDLHapticRectSpec.m in Sources */, 162E82EA1A9BDE8B00906325 /* SDLLanguageSpec.m in Sources */, 5D76E3291D3D0A8800647CFA /* SDLFakeViewControllerPresenter.m in Sources */, 162E83331A9BDE8B00906325 /* SDLPerformInteractionSpec.m in Sources */, @@ -5210,6 +5281,7 @@ 162E83281A9BDE8B00906325 /* SDLDeleteCommandSpec.m in Sources */, 162E83531A9BDE8B00906325 /* SDLDiagnosticMessageResponseSpec.m in Sources */, 162E83671A9BDE8B00906325 /* SDLSliderResponseSpec.m in Sources */, + 8877F5F11F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m in Sources */, 162E836C1A9BDE8B00906325 /* SDLSystemRequestResponseSpec.m in Sources */, 162E833C1A9BDE8B00906325 /* SDLSetMediaClockTimerSpec.m in Sources */, 162E83061A9BDE8B00906325 /* SDLVehicleDataEventStatusSpec.m in Sources */, @@ -5364,7 +5436,9 @@ 162E838C1A9BDE8B00906325 /* SDLSoftButtonSpec.m in Sources */, 5DA23FF81F2FAF2D009C0313 /* SDLControlFramePayloadRPCStartServiceAckSpec.m in Sources */, 162E83191A9BDE8B00906325 /* SDLOnLanguageChangeSpec.m in Sources */, + 5DADA7781F4E059E0084D17D /* SDLRectangleSpec.m in Sources */, 5DB1BCDD1D243DC3002FFC37 /* SDLLifecycleConfigurationSpec.m in Sources */, + EED5CA0A1F4D206800F04000 /* SDLRTPH264PacketizerSpec.m in Sources */, 162E83611A9BDE8B00906325 /* SDLSetAppIconResponseSpec.m in Sources */, 162E83471A9BDE8B00906325 /* SDLUnsubscribeVehicleDataSpec.m in Sources */, 162E839A1A9BDE8B00906325 /* SDLRPCMessageSpec.m in Sources */, @@ -5405,6 +5479,7 @@ 162E83321A9BDE8B00906325 /* SDLPerformAudioPassThruSpec.m in Sources */, 162E830B1A9BDE8B00906325 /* SDLVrCapabilitiesSpec.m in Sources */, 162E83081A9BDE8B00906325 /* SDLVehicleDataResultCodeSpec.m in Sources */, + 8877F5EB1F34A3BE00DC128A /* SDLSendHapticDataSpec.m in Sources */, 162E83621A9BDE8B00906325 /* SDLSetDisplayLayoutResponseSpec.m in Sources */, 162E832E1A9BDE8B00906325 /* SDLEndAudioPassThruSpec.m in Sources */, 1680B1191A9CD7AD00DBD79E /* SDLV2ProtocolMessageSpec.m in Sources */, @@ -5437,6 +5512,7 @@ 5DB1BCD31D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m in Sources */, 5D0A9F971F1559EC00CC80DD /* SDLSystemCapabilitySpec.m in Sources */, 162E82F41A9BDE8B00906325 /* SDLPrimaryAudioSource.m in Sources */, + EED5CA041F4D1D5E00F04000 /* SDLH264ByteStreamPacketizerSpec.m in Sources */, 5DBAE0AB1D3588AC00CE00BF /* SDLNotificationDispatcherSpec.m in Sources */, 162E83461A9BDE8B00906325 /* SDLUnsubscribeButtonSpec.m in Sources */, 162E82EB1A9BDE8B00906325 /* SDLLayoutModeSpec.m in Sources */, diff --git a/SmartDeviceLink.podspec b/SmartDeviceLink.podspec index dcbcea7f3..3aca1eaeb 100644 --- a/SmartDeviceLink.podspec +++ b/SmartDeviceLink.podspec @@ -104,6 +104,7 @@ ss.public_header_files = [ 'SmartDeviceLink/SDLGetWaypointsResponse.h', 'SmartDeviceLink/SDLGlobalProperty.h', 'SmartDeviceLink/SDLGPSData.h', +'SmartDeviceLink/SDLHapticRect.h', 'SmartDeviceLink/SDLHeadLampStatus.h', 'SmartDeviceLink/SDLHMICapabilities.h', 'SmartDeviceLink/SDLHMILevel.h', @@ -222,6 +223,8 @@ ss.public_header_files = [ 'SmartDeviceLink/SDLScrollableMessage.h', 'SmartDeviceLink/SDLScrollableMessageResponse.h', 'SmartDeviceLink/SDLSecurityType.h', +'SmartDeviceLink/SDLSendHapticData.h', +'SmartDeviceLink/SDLSendHapticDataResponse.h', 'SmartDeviceLink/SDLSendLocation.h', 'SmartDeviceLink/SDLSendLocationResponse.h', 'SmartDeviceLink/SDLSetAppIcon.h', diff --git a/SmartDeviceLink/SDLControlFramePayloadAudioStartServiceAck.m b/SmartDeviceLink/SDLControlFramePayloadAudioStartServiceAck.m index ac969d07b..be5460bb4 100644 --- a/SmartDeviceLink/SDLControlFramePayloadAudioStartServiceAck.m +++ b/SmartDeviceLink/SDLControlFramePayloadAudioStartServiceAck.m @@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN _mtu = SDLControlFrameInt64NotFound; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadEndService.m b/SmartDeviceLink/SDLControlFramePayloadEndService.m index 8399da517..372400e7d 100644 --- a/SmartDeviceLink/SDLControlFramePayloadEndService.m +++ b/SmartDeviceLink/SDLControlFramePayloadEndService.m @@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN _hashId = SDLControlFrameInt32NotFound; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadNak.m b/SmartDeviceLink/SDLControlFramePayloadNak.m index 4402dea47..15f5c517c 100644 --- a/SmartDeviceLink/SDLControlFramePayloadNak.m +++ b/SmartDeviceLink/SDLControlFramePayloadNak.m @@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN self = [super init]; if (!self) return nil; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadRPCStartService.m b/SmartDeviceLink/SDLControlFramePayloadRPCStartService.m index 222370d24..95245e93e 100644 --- a/SmartDeviceLink/SDLControlFramePayloadRPCStartService.m +++ b/SmartDeviceLink/SDLControlFramePayloadRPCStartService.m @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN self = [super init]; if (!self) return nil; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadRPCStartServiceAck.m b/SmartDeviceLink/SDLControlFramePayloadRPCStartServiceAck.m index e4b8347fa..cc2c2c3a5 100644 --- a/SmartDeviceLink/SDLControlFramePayloadRPCStartServiceAck.m +++ b/SmartDeviceLink/SDLControlFramePayloadRPCStartServiceAck.m @@ -43,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN _hashId = SDLControlFrameInt32NotFound; _mtu = SDLControlFrameInt64NotFound; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadVideoStartService.m b/SmartDeviceLink/SDLControlFramePayloadVideoStartService.m index 800c3ace8..d33698ebb 100644 --- a/SmartDeviceLink/SDLControlFramePayloadVideoStartService.m +++ b/SmartDeviceLink/SDLControlFramePayloadVideoStartService.m @@ -44,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN _height = SDLControlFrameInt32NotFound; _width = SDLControlFrameInt32NotFound; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLControlFramePayloadVideoStartServiceAck.m b/SmartDeviceLink/SDLControlFramePayloadVideoStartServiceAck.m index d74d1c68c..f16f4b108 100644 --- a/SmartDeviceLink/SDLControlFramePayloadVideoStartServiceAck.m +++ b/SmartDeviceLink/SDLControlFramePayloadVideoStartServiceAck.m @@ -45,7 +45,7 @@ _height = SDLControlFrameInt32NotFound; _width = SDLControlFrameInt32NotFound; - if (data != nil) { + if (data.length > 0) { [self sdl_parse:data]; } diff --git a/SmartDeviceLink/SDLFunctionID.m b/SmartDeviceLink/SDLFunctionID.m index 147706405..c18cdc2cb 100644 --- a/SmartDeviceLink/SDLFunctionID.m +++ b/SmartDeviceLink/SDLFunctionID.m @@ -75,6 +75,7 @@ NS_ASSUME_NONNULL_BEGIN @46: SDLNameSubscribeWayPoints, @47: SDLNameUnsubscribeWayPoints, @48: SDLNameGetSystemCapability, + @49: SDLNameSendHapticData, @32768: SDLNameOnHMIStatus, @32769: SDLNameOnAppInterfaceUnregistered, @32770: SDLNameOnButtonEvent, diff --git a/SmartDeviceLink/SDLH264ByteStreamPacketizer.h b/SmartDeviceLink/SDLH264ByteStreamPacketizer.h new file mode 100644 index 000000000..5b6d57928 --- /dev/null +++ b/SmartDeviceLink/SDLH264ByteStreamPacketizer.h @@ -0,0 +1,17 @@ +// +// SDLH264ByteStreamPacketizer.h +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/11/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "SDLH264Packetizer.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLH264ByteStreamPacketizer : NSObject <SDLH264Packetizer> +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLH264ByteStreamPacketizer.m b/SmartDeviceLink/SDLH264ByteStreamPacketizer.m new file mode 100644 index 000000000..c229b0438 --- /dev/null +++ b/SmartDeviceLink/SDLH264ByteStreamPacketizer.m @@ -0,0 +1,51 @@ +// +// SDLH264ByteStreamPacketizer.m +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/11/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "SDLH264ByteStreamPacketizer.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLH264ByteStreamPacketizer () +@property (nonatomic) NSData *startCode; +@end + +@implementation SDLH264ByteStreamPacketizer + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + // This is the start code that we will write to the elementary stream before every NAL unit + UInt8 startCode[] = {0x00, 0x00, 0x00, 0x01}; + _startCode = [[NSData alloc] initWithBytes:startCode length:4]; + + return self; +} + +- (nullable NSArray<NSData *> *)createPackets:(NSArray<NSData *> *)nalUnits + presentationTimestamp:(double)presentationTimestamp { + NSMutableArray *array = [NSMutableArray arrayWithCapacity:1]; + NSMutableData *elementaryStream = [NSMutableData data]; + + // Note: this packetizer consolidates all NAL units into one NSData object + // to keep compatibility with previous implementation. + for (NSData *nalUnit in nalUnits) { + [elementaryStream appendData:self.startCode]; + [elementaryStream appendData:nalUnit]; + } + + [array addObject:elementaryStream]; + return [array copy]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLH264Packetizer.h b/SmartDeviceLink/SDLH264Packetizer.h new file mode 100644 index 000000000..45c5e8d5c --- /dev/null +++ b/SmartDeviceLink/SDLH264Packetizer.h @@ -0,0 +1,33 @@ +// +// SDLH264Packetizer.h +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/11/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +@protocol SDLH264Packetizer + +/** + * Creates packets from given H.264 NAL units and presentation timestamp. + * + * @param nalUnits List of NAL units to create packets. + * @param presentationTimestamp Presentation timestamp associated to + * the NAL units, in seconds. + * + * @return List of NSData. Each NSData holds a packet. + * + * @warning This method cannot be called more than once with same pts value. + * All NAL units that belongs to a frame should be included in + * nalUnits array. + */ +- (nullable NSArray<NSData *> *)createPackets:(NSArray<NSData *> *)nalUnits + presentationTimestamp:(double)presentationTimestamp; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLHapticRect.h b/SmartDeviceLink/SDLHapticRect.h new file mode 100644 index 000000000..295ea55af --- /dev/null +++ b/SmartDeviceLink/SDLHapticRect.h @@ -0,0 +1,34 @@ +// +// SDLHapticRect.h +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/3/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <UIKit/UIKit.h> + +#import "SDLRPCStruct.h" + +@class SDLRectangle; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Defines spatial for each user control object for video streaming application + */ +@interface SDLHapticRect : SDLRPCStruct + +- (instancetype)initWithId:(UInt32)id rect:(SDLRectangle *)rect; + +/** + * A user control spatial identifier + * Required, Integer, 0 - 2,000,000,000 + */ +@property (strong, nonatomic) NSNumber<SDLUInt> *id; + +@property (strong, nonatomic) SDLRectangle *rect; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLHapticRect.m b/SmartDeviceLink/SDLHapticRect.m new file mode 100644 index 000000000..330756d4c --- /dev/null +++ b/SmartDeviceLink/SDLHapticRect.m @@ -0,0 +1,48 @@ +// +// SDLHapticRect.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/3/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import "NSMutableDictionary+Store.h" +#import "SDLHapticRect.h" +#import "SDLNames.h" +#import "SDLRectangle.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation SDLHapticRect + +- (instancetype)initWithId:(UInt32)id rect:(nonnull SDLRectangle *)rect { + self = [self init]; + if (!self) { + return nil; + } + + self.id = @(id); + self.rect = rect; + + return self; +} + +- (void)setId:(NSNumber<SDLInt> *)id { + [store sdl_setObject:id forName:SDLNameId]; +} + +- (NSNumber<SDLInt> *)id { + return [store sdl_objectForName:SDLNameId]; +} + +- (void)setRect:(SDLRectangle *)rect { + [store sdl_setObject:rect forName:SDLNameRect]; +} + +- (SDLRectangle *)rect { + return [store sdl_objectForName:SDLNameRect ofClass:SDLRectangle.class]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLMetadataType.h b/SmartDeviceLink/SDLMetadataType.h index cc6518b1c..c5a309412 100644 --- a/SmartDeviceLink/SDLMetadataType.h +++ b/SmartDeviceLink/SDLMetadataType.h @@ -73,8 +73,3 @@ extern SDLMetadataType const SDLMetadataTypeWeatherTerm; */ extern SDLMetadataType const SDLMetadataTypeHumidity; -/** - The data in this field is not of a common type or should not be processed. Any time a field does not have a type parameter it is considered as the none type. - */ -extern SDLMetadataType const SDLMetadataTypeNone; - diff --git a/SmartDeviceLink/SDLMetadataType.m b/SmartDeviceLink/SDLMetadataType.m index ad032569b..22e846ccc 100644 --- a/SmartDeviceLink/SDLMetadataType.m +++ b/SmartDeviceLink/SDLMetadataType.m @@ -20,4 +20,3 @@ SDLMetadataType const SDLMetadataTypeMaximumTemperature = @"maximumTemperature"; SDLMetadataType const SDLMetadataTypeMinimumTemperature = @"minimumTemperature"; SDLMetadataType const SDLMetadataTypeWeatherTerm = @"weatherTerm"; SDLMetadataType const SDLMetadataTypeHumidity = @"humidity"; -SDLMetadataType const SDLMetadataTypeNone = @"none"; diff --git a/SmartDeviceLink/SDLNames.h b/SmartDeviceLink/SDLNames.h index 0e0165755..91bdbab8c 100644 --- a/SmartDeviceLink/SDLNames.h +++ b/SmartDeviceLink/SDLNames.h @@ -138,11 +138,14 @@ extern SDLName const SDLNameGetWayPointsEnabled; extern SDLName const SDLNameGPS; extern SDLName const SDLNameGraphic; extern SDLName const SDLNameGraphicSupported; +extern SDLName const SDLNameHapticRectData; +extern SDLName const SDLNameHapticSpatialDataSupported; extern SDLName const SDLNameHardware; extern SDLName const SDLNameHashId; extern SDLName const SDLNameHDOP; extern SDLName const SDLNameHeading; extern SDLName const SDLNameHeadLampStatus; +extern SDLName const SDLNameHeight; extern SDLName const SDLNameHelpPrompt; extern SDLName const SDLNameHighBeamsOn; extern SDLName const SDLNameHMIDisplayLanguage; @@ -322,6 +325,7 @@ extern SDLName const SDLNameReadDID; extern SDLName const SDLNameRearLeftDoorAjar; extern SDLName const SDLNameRearRightDoorAjar; extern SDLName const SDLNameReason; +extern SDLName const SDLNameRect; extern SDLName const SDLNameRegisterAppInterface; extern SDLName const SDLNameRequest; extern SDLName const SDLNameRequestType; @@ -354,6 +358,8 @@ extern SDLName const SDLNameSecondaryImage; extern SDLName const SDLNameSecondaryText; extern SDLName const SDLNameSecond; extern SDLName const SDLNameSeconds; +extern SDLName const SDLNameSendHapticData; +extern SDLName const SDLNameSendHapticDataResponse; extern SDLName const SDLNameSendLocation; extern SDLName const SDLNameSendLocationEnabled; extern SDLName const SDLNameSetAppIcon; diff --git a/SmartDeviceLink/SDLNames.m b/SmartDeviceLink/SDLNames.m index 79cbaf797..084d3397f 100644 --- a/SmartDeviceLink/SDLNames.m +++ b/SmartDeviceLink/SDLNames.m @@ -135,11 +135,14 @@ SDLName const SDLNameGetWayPointsEnabled = @"getWayPointsEnabled"; SDLName const SDLNameGPS = @"gps"; SDLName const SDLNameGraphic = @"graphic"; SDLName const SDLNameGraphicSupported = @"graphicSupported"; +SDLName const SDLNameHapticRectData = @"hapticRectData"; +SDLName const SDLNameHapticSpatialDataSupported = @"hapticSpatialDataSupported"; SDLName const SDLNameHardware = @"hardware"; SDLName const SDLNameHashId = @"hashID"; SDLName const SDLNameHDOP = @"hdop"; SDLName const SDLNameHeading = @"heading"; SDLName const SDLNameHeadLampStatus = @"headLampStatus"; +SDLName const SDLNameHeight = @"height"; SDLName const SDLNameHelpPrompt = @"helpPrompt"; SDLName const SDLNameHighBeamsOn = @"highBeamsOn"; SDLName const SDLNameHMIDisplayLanguage = @"hmiDisplayLanguage"; @@ -316,6 +319,7 @@ SDLName const SDLNameReadDID = @"ReadDID"; SDLName const SDLNameRearLeftDoorAjar = @"rearLeftDoorAjar"; SDLName const SDLNameRearRightDoorAjar = @"rearRightDoorAjar"; SDLName const SDLNameReason = @"reason"; +SDLName const SDLNameRect = @"rect"; SDLName const SDLNameRegisterAppInterface = @"RegisterAppInterface"; SDLName const SDLNameRequest = @"request"; SDLName const SDLNameRequestType = @"requestType"; @@ -348,6 +352,8 @@ SDLName const SDLNameSecondaryImage = @"secondaryImage"; SDLName const SDLNameSecondaryText = @"secondaryText"; SDLName const SDLNameSecond = @"second"; SDLName const SDLNameSeconds = @"seconds"; +SDLName const SDLNameSendHapticData = @"SendHapticData"; +SDLName const SDLNameSendHapticDataResponse = @"SendHapticDataResponse"; SDLName const SDLNameSendLocation = @"SendLocation"; SDLName const SDLNameSendLocationEnabled = @"sendLocationEnabled"; SDLName const SDLNameSetAppIcon = @"SetAppIcon"; diff --git a/SmartDeviceLink/SDLNotificationConstants.h b/SmartDeviceLink/SDLNotificationConstants.h index 9c9edca13..d313edb39 100644 --- a/SmartDeviceLink/SDLNotificationConstants.h +++ b/SmartDeviceLink/SDLNotificationConstants.h @@ -114,6 +114,7 @@ extern SDLNotificationName const SDLDidReceiveReadDIDResponse; extern SDLNotificationName const SDLDidReceiveRegisterAppInterfaceResponse; extern SDLNotificationName const SDLDidReceiveResetGlobalPropertiesResponse; extern SDLNotificationName const SDLDidReceiveScrollableMessageResponse; +extern SDLNotificationName const SDLDidReceiveSendHapticDataResponse; extern SDLNotificationName const SDLDidReceiveSendLocationResponse; extern SDLNotificationName const SDLDidReceiveSetAppIconResponse; extern SDLNotificationName const SDLDidReceiveSetDisplayLayoutResponse; diff --git a/SmartDeviceLink/SDLNotificationConstants.m b/SmartDeviceLink/SDLNotificationConstants.m index b444a8857..1952f397b 100644 --- a/SmartDeviceLink/SDLNotificationConstants.m +++ b/SmartDeviceLink/SDLNotificationConstants.m @@ -47,6 +47,7 @@ SDLNotificationName const SDLDidReceiveReadDIDResponse = @"com.sdl.response.read SDLNotificationName const SDLDidReceiveRegisterAppInterfaceResponse = @"com.sdl.response.registerAppInterface"; SDLNotificationName const SDLDidReceiveResetGlobalPropertiesResponse = @"com.sdl.response.resetGlobalProperties"; SDLNotificationName const SDLDidReceiveScrollableMessageResponse = @"com.sdl.response.scrollableMessage"; +SDLNotificationName const SDLDidReceiveSendHapticDataResponse = @"com.sdl.response.sendHapticData"; SDLNotificationName const SDLDidReceiveSendLocationResponse = @"com.sdl.response.sendLocation"; SDLNotificationName const SDLDidReceiveSetAppIconResponse = @"com.sdl.response.setAppIcon"; SDLNotificationName const SDLDidReceiveSetDisplayLayoutResponse = @"com.sdl.response.setDisplayLayout"; @@ -117,6 +118,7 @@ SDLNotificationName const SDLDidReceiveWaypointNotification = @"com.sdl.notifica SDLDidReceiveRegisterAppInterfaceResponse, SDLDidReceiveResetGlobalPropertiesResponse, SDLDidReceiveScrollableMessageResponse, + SDLDidReceiveSendHapticDataResponse, SDLDidReceiveSendLocationResponse, SDLDidReceiveSetAppIconResponse, SDLDidReceiveSetDisplayLayoutResponse, diff --git a/SmartDeviceLink/SDLNotificationDispatcher.m b/SmartDeviceLink/SDLNotificationDispatcher.m index a26e46a4a..3da6e790a 100644 --- a/SmartDeviceLink/SDLNotificationDispatcher.m +++ b/SmartDeviceLink/SDLNotificationDispatcher.m @@ -186,6 +186,10 @@ NS_ASSUME_NONNULL_BEGIN [self postRPCResponseNotification:SDLDidReceiveScrollableMessageResponse response:response]; } +- (void)onSendHapticDataResponse:(SDLSendHapticDataResponse *)response { + [self postRPCResponseNotification:SDLDidReceiveSendHapticDataResponse response:response]; +} + - (void)onSendLocationResponse:(SDLSendLocationResponse *)response { [self postRPCResponseNotification:SDLDidReceiveSendLocationResponse response:response]; } diff --git a/SmartDeviceLink/SDLProxyListener.h b/SmartDeviceLink/SDLProxyListener.h index 42c6782bf..64c177579 100644 --- a/SmartDeviceLink/SDLProxyListener.h +++ b/SmartDeviceLink/SDLProxyListener.h @@ -50,6 +50,7 @@ @class SDLRegisterAppInterfaceResponse; @class SDLResetGlobalPropertiesResponse; @class SDLScrollableMessageResponse; +@class SDLSendHapticDataResponse; @class SDLSendLocationResponse; @class SDLSetAppIconResponse; @class SDLSetDisplayLayoutResponse; @@ -126,6 +127,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)onRegisterAppInterfaceResponse:(SDLRegisterAppInterfaceResponse *)response; - (void)onResetGlobalPropertiesResponse:(SDLResetGlobalPropertiesResponse *)response; - (void)onScrollableMessageResponse:(SDLScrollableMessageResponse *)response; +- (void)onSendHapticDataResponse:(SDLSendHapticDataResponse *)response; - (void)onSendLocationResponse:(SDLSendLocationResponse *)response; - (void)onSetAppIconResponse:(SDLSetAppIconResponse *)response; - (void)onSetDisplayLayoutResponse:(SDLSetDisplayLayoutResponse *)response; diff --git a/SmartDeviceLink/SDLRTPH264Packetizer.h b/SmartDeviceLink/SDLRTPH264Packetizer.h new file mode 100644 index 000000000..b909fa1d1 --- /dev/null +++ b/SmartDeviceLink/SDLRTPH264Packetizer.h @@ -0,0 +1,43 @@ +// +// SDLRTPH264Packetizer.h +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/11/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "SDLH264Packetizer.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLRTPH264Packetizer : NSObject <SDLH264Packetizer> + +/** + * Payload Type (PT) of RTP header field. + * + * PT field identifies the format of the RTP payload ([5.1] in RFC 3550). + * RFC 6184 doesn’t specify this value and says it "has to be performed + * either through the profile used or in a dynamic way" in [5.1]. + * + * In our spec, this value is chosen from range 96-127 (which are for + * dynamic assignment) and will be ignored by HMI. Refer to the proposal: + * https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0048-H264-over-RTP-support-for-video-streaming.md + * + * @note Default value is 96. + */ +@property (assign, nonatomic) UInt8 payloadType; + +/** + * SSRC of RTP header field. + * + * SSRC field identifies the source of a stream and it should be + * chosen randomly (see section 3 and 5.1 in RFC 3550). + * + * @note A random value is generated and used as default. + */ +@property (assign, nonatomic) UInt32 ssrc; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLRTPH264Packetizer.m b/SmartDeviceLink/SDLRTPH264Packetizer.m new file mode 100644 index 000000000..06fc645ad --- /dev/null +++ b/SmartDeviceLink/SDLRTPH264Packetizer.m @@ -0,0 +1,246 @@ +// +// SDLRTPH264Packetizer.m +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/11/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +/* + * Note for testing. + * The RTP stream generated by this packetizer can be tested with GStreamer (1.4 or later). + * Assuming that "VideoStreamPort" is configured as 5050 in smartDeviceLink.ini, here is the + * GStreamer pipeline that receives the stream, decode it and render it: + * + * $ gst-launch-1.0 souphttpsrc location=http://127.0.0.1:5050 ! "application/x-rtp-stream" ! rtpstreamdepay ! "application/x-rtp,media=(string)video,clock-rate=90000,encoding-name=(string)H264" ! rtph264depay ! "video/x-h264, stream-format=(string)avc, alignment=(string)au" ! avdec_h264 ! videoconvert ! ximagesink sync=false + */ + +#import <Foundation/Foundation.h> +#import "SDLRTPH264Packetizer.h" +#import "SDLLogMacros.h" + +const NSUInteger FrameLengthLen = 2; +const NSUInteger MaxRTPPacketSize = 65535; // because length field is two bytes (RFC 4571) +const NSUInteger RTPHeaderLen = 12; +const UInt8 DefaultPayloadType = 96; +const NSUInteger FragmentationUnitIndicatorLen = 1; +const NSUInteger FragmentationUnitHeaderLen = 1; +const UInt8 FragmentationUnitVersionA = 0x1C; +const NSUInteger ClockRate = 90000; // we use 90 kHz clock rate ([5.1] in RFC 6184) + +/** + * write 2-byte value into buffer in network byte order + * + * @param buffer buffer to write the value + * @param value value to write + */ +static inline void sdl_writeShortInNetworkByteOrder(UInt8 *buffer, UInt16 value) { + buffer[0] = (value >> 8) & 0xFF; + buffer[1] = value & 0xFF; +} + +/** + * write 4-byte value into buffer in network byte order + * + * @param buffer buffer to write the value + * @param value value to write + */ +static inline void sdl_writeLongInNetworkByteOrder(UInt8 *buffer, UInt32 value) { + buffer[0] = (value >> 24) & 0xFF; + buffer[1] = (value >> 16) & 0xFF; + buffer[2] = (value >> 8) & 0xFF; + buffer[3] = value & 0xFF; +} + +NS_ASSUME_NONNULL_BEGIN + +@interface SDLRTPH264Packetizer () +@property (assign, nonatomic) UInt32 initialTimestamp; +@property (assign, nonatomic) UInt16 sequenceNum; +@end + +@implementation SDLRTPH264Packetizer + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _payloadType = DefaultPayloadType; + // initial value of the sequence number and timestamp should be random ([5.1] in RFC3550) + _initialTimestamp = arc4random_uniform(UINT32_MAX); + _sequenceNum = (UInt16)arc4random_uniform(UINT16_MAX); + _ssrc = arc4random_uniform(UINT32_MAX); + + return self; +} + +- (void)setPayloadType:(UInt8)payloadType { + if (payloadType <= 127) { + _payloadType = payloadType; + } else { + _payloadType = DefaultPayloadType; + } +} + +- (nullable NSArray<NSData *> *)createPackets:(NSArray<NSData *> *)nalUnits + presentationTimestamp:(double)presentationTimestamp { + NSMutableArray *rtpFrames = [NSMutableArray array]; + NSUInteger nalUnitsCount = nalUnits.count; + + for (NSUInteger i = 0; i < nalUnitsCount; i++) { + NSData *nalUnit = nalUnits[i]; + BOOL isLast = ((i + 1) == nalUnitsCount); + + if (RTPHeaderLen + nalUnit.length > MaxRTPPacketSize) { + // Split into multiple Fragmentation Units ([5.8] in RFC 6184) + if (![self sdl_addRTPFramesWithFragmentationUnits:rtpFrames nalUnit:nalUnit presentationTimestamp:presentationTimestamp isLast:isLast]) { + return nil; + } + } else { + // Use Single NAL Unit Packet ([5.6] in RFC 6184) + if (![self sdl_addRTPFrameWithSingleNALUnit:rtpFrames nalUnit:nalUnit presentationTimestamp:presentationTimestamp isLast:isLast]) { + return nil; + } + } + } + + return [rtpFrames copy]; +} + +/** + * Create and add a RTP frame from a NAL unit without splitting + * + * @param rtpFrames the array to which created RTP frame is added + * @param nalUnit NAL unit to create RTP frame from + * @param presentationTimestamp presentation timestamp in seconds + * @param isLast mark YES if this is the last NAL unit of a video frame + * + * @return YES if successful, NO if memory error occurred + */ +- (BOOL)sdl_addRTPFrameWithSingleNALUnit:(NSMutableArray<NSData *> *)rtpFrames + nalUnit:(NSData *)nalUnit + presentationTimestamp:(double)presentationTimestamp + isLast:(BOOL)isLast { + NSUInteger nalUnitLength = nalUnit.length; + NSUInteger packetSize = RTPHeaderLen + nalUnitLength; + NSUInteger frameSize = FrameLengthLen + packetSize; + + NSAssert(RTPHeaderLen + nalUnitLength <= MaxRTPPacketSize, @"This NAL unit doesn't fit into single RTP packet"); + + UInt8 *buffer = malloc(frameSize); + if (buffer == NULL) { + SDLLogE(@"malloc() error"); + return NO; + } + UInt8 *writePointer = buffer; + + writePointer += [self sdl_writeFrameHeader:writePointer packetSize:packetSize]; + writePointer += [self sdl_writeRTPHeader:writePointer marker:isLast presentationTimestamp:presentationTimestamp]; + [nalUnit getBytes:writePointer length:nalUnitLength]; + + NSData *rtpFrame = [NSData dataWithBytesNoCopy:buffer length:frameSize]; + [rtpFrames addObject:rtpFrame]; + + return YES; +} + +/** + * Create and add a RTP frames by splitting a NAL unit into multiple Fragmentation Units + * + * @param rtpFrames the array to which created RTP frames are added + * @param nalUnit NAL unit to create RTP frames from + * @param presentationTimestamp presentation timestamp in seconds + * @param isLast mark YES if this is the last NAL unit of a video frame + * + * @return YES if successful, NO if memory error occurred + */ +- (BOOL)sdl_addRTPFramesWithFragmentationUnits:(NSMutableArray<NSData *> *)rtpFrames + nalUnit:(NSData *)nalUnit + presentationTimestamp:(double)presentationTimestamp + isLast:(BOOL)isLast { + UInt8 firstByte; + [nalUnit getBytes:&firstByte length:1]; + BOOL isFirstFragment = YES; + BOOL isLastFragment = NO; + NSUInteger nalUnitLength = nalUnit.length; + NSUInteger offset = 1; // we have already read the first byte + + while (offset < nalUnitLength) { + NSUInteger payloadLength = MaxRTPPacketSize - (RTPHeaderLen + FragmentationUnitIndicatorLen + FragmentationUnitHeaderLen); + if (nalUnitLength - offset <= payloadLength) { + payloadLength = nalUnitLength - offset; + isLastFragment = YES; + } + NSUInteger packetSize = RTPHeaderLen + FragmentationUnitIndicatorLen + FragmentationUnitHeaderLen + payloadLength; + NSUInteger frameSize = FrameLengthLen + packetSize; + + UInt8 *buffer = malloc(frameSize); + if (buffer == NULL) { + SDLLogE(@"malloc() error"); + return NO; + } + UInt8 *writePointer = buffer; + + writePointer += [self sdl_writeFrameHeader:writePointer packetSize:packetSize]; + writePointer += [self sdl_writeRTPHeader:writePointer marker:isLast presentationTimestamp:presentationTimestamp]; + + // Fragmentation Unit indicator + *writePointer++ = (firstByte & 0xE0) | FragmentationUnitVersionA; + // Fragmentation Unit header + *writePointer++ = (isFirstFragment ? 0x80 : isLastFragment ? 0x40 : 0) | (firstByte & 0x1F); + // Fragmentation Unit payload + [nalUnit getBytes:writePointer range:NSMakeRange(offset, payloadLength)]; + offset += payloadLength; + + NSData *rtpFrame = [NSData dataWithBytesNoCopy:buffer length:frameSize]; + [rtpFrames addObject:rtpFrame]; + + isFirstFragment = NO; + } + + return YES; +} + +/** + * Write RTP Frame header (length field) to supplied buffer. + * + * @param frameHeaderBuffer the buffer in which a header is written. + * @param packetSize size of a RTP packet that follows to this frame header. + * + * @return number of bytes written, which is always 2. + */ +- (NSUInteger)sdl_writeFrameHeader:(UInt8 *)frameHeaderBuffer packetSize:(NSUInteger)packetSize { + NSAssert(packetSize <= MaxRTPPacketSize, @"RTP packet is too big"); + sdl_writeShortInNetworkByteOrder(frameHeaderBuffer, (UInt16)packetSize); + return FrameLengthLen; +} + +/** + * Write RTP header to supplied buffer. + * + * @param rtpHeaderBuffer the buffer in which a header is written. + * @param isLast whether this is the last packet of an access unit. + * @param presentationTimestamp presentation timestamp in seconds. + * + * @return number of bytes written, which is always 12. + */ +- (NSUInteger)sdl_writeRTPHeader:(UInt8 *)rtpHeaderBuffer marker:(BOOL)isLast presentationTimestamp:(double)presentationTimestamp { + UInt32 presentationTimestampIn90kHz = presentationTimestamp * ClockRate; + + // Version = 2, Padding = 0, Extension = 0, CSRC count = 0 + rtpHeaderBuffer[0] = 0x80; + // Marker = isLast, Payload type = self.payloadType + rtpHeaderBuffer[1] = (isLast ? 0x80 : 0) | (self.payloadType & 0x7F); + sdl_writeShortInNetworkByteOrder(rtpHeaderBuffer + 2, self.sequenceNum); + sdl_writeLongInNetworkByteOrder(rtpHeaderBuffer + 4, self.initialTimestamp + presentationTimestampIn90kHz); + sdl_writeLongInNetworkByteOrder(rtpHeaderBuffer + 8, self.ssrc); + + self.sequenceNum++; + return RTPHeaderLen; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLRectangle.h b/SmartDeviceLink/SDLRectangle.h new file mode 100644 index 000000000..5395f6315 --- /dev/null +++ b/SmartDeviceLink/SDLRectangle.h @@ -0,0 +1,56 @@ +// +// SDLRectangle.h +// SmartDeviceLink-iOS +// +// Created by Joel Fischer on 8/23/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <SmartDeviceLink/SmartDeviceLink.h> + +@interface SDLRectangle : SDLRPCStruct + +/** + Create a Rectangle + + @param x The top-left x value + @param y The top-left y value + @param width The width + @param height The height + @return An new SDLRectangle object + */ +- (instancetype)initWithX:(float)x y:(float)y width:(float)width height:(float)height; + +/** + Create a Rectangle from a CGRect + + @param rect The rectangle to use + @return An new SDLRectangle object + */ +- (instancetype)initWithCGRect:(CGRect)rect; + +/** + * The X-coordinate of the user control + * Required, Float + */ +@property (strong, nonatomic) NSNumber<SDLFloat> *x; + +/** + * The Y-coordinate of the user control + * Required, Float + */ +@property (strong, nonatomic) NSNumber<SDLFloat> *y; + +/** + * The width of the user control's bounding rectangle + * Required, Float + */ +@property (strong, nonatomic) NSNumber<SDLFloat> *width; + +/** + * The height of the user control's bounding rectangle + * Required, Float + */ +@property (strong, nonatomic) NSNumber<SDLFloat> *height; + +@end diff --git a/SmartDeviceLink/SDLRectangle.m b/SmartDeviceLink/SDLRectangle.m new file mode 100644 index 000000000..6a82312b9 --- /dev/null +++ b/SmartDeviceLink/SDLRectangle.m @@ -0,0 +1,63 @@ +// +// SDLRectangle.m +// SmartDeviceLink-iOS +// +// Created by Joel Fischer on 8/23/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import "NSMutableDictionary+Store.h" +#import "SDLRectangle.h" +#import "SDLNames.h" + +@implementation SDLRectangle + +- (instancetype)initWithX:(float)x y:(float)y width:(float)width height:(float)height { + self = [self init]; + if (!self) { return nil; } + + self.x = @(x); + self.y = @(y); + self.width = @(width); + self.height = @(height); + + return self; +} + +- (instancetype)initWithCGRect:(CGRect)rect { + return [self initWithX:rect.origin.x y:rect.origin.y width:rect.size.width height:rect.size.height]; +} + +- (void)setX:(NSNumber<SDLFloat> *)x { + [store sdl_setObject:x forName:SDLNameX]; +} + +- (NSNumber<SDLFloat> *)x { + return [store sdl_objectForName:SDLNameX]; +} + +- (void)setY:(NSNumber<SDLFloat> *)y { + [store sdl_setObject:y forName:SDLNameY]; +} + +- (NSNumber<SDLFloat> *)y { + return [store sdl_objectForName:SDLNameY]; +} + +- (void)setWidth:(NSNumber<SDLFloat> *)width { + [store sdl_setObject:width forName:SDLNameWidth]; +} + +- (NSNumber<SDLFloat> *)width { + return [store sdl_objectForName:SDLNameWidth]; +} + +- (void)setHeight:(NSNumber<SDLFloat> *)height { + [store sdl_setObject:height forName:SDLNameHeight]; +} + +- (NSNumber<SDLFloat> *)height { + return [store sdl_objectForName:SDLNameHeight]; +} + +@end diff --git a/SmartDeviceLink/SDLSendHapticData.h b/SmartDeviceLink/SDLSendHapticData.h new file mode 100644 index 000000000..c4c0f237c --- /dev/null +++ b/SmartDeviceLink/SDLSendHapticData.h @@ -0,0 +1,37 @@ +// +// SDLSendHapticData.h +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/3/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "SDLRPCRequest.h" + +@class SDLHapticRect; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Sends the spatial data gathered from SDLCarWindow or VirtualDisplayEncoder to the HMI. This data will be utilized by the HMI to determine how and when haptic events should occur. + */ +@interface SDLSendHapticData : SDLRPCRequest + +/** + * Constructs a new SDLSendHapticData object indicated by the hapticSpatialData parameter + * + * @param hapticRectData Array of spatial data structures + */ +- (instancetype)initWithHapticRectData:(NSArray<SDLHapticRect *> *)hapticRectData; + +/** + * Array of spatial data structures that represent the locations of all user controls present on the HMI. This data should be updated if/when the application presents a new screen. When a request is sent, if successful, it will replace all spatial data previously sent through RPC. If an empty array is sent, the existing spatial data will be cleared + * + * Optional, Array of SDLHapticRect, Array size 0 - 1,000 + */ +@property (strong, nonatomic, nullable) NSArray<SDLHapticRect *> *hapticRectData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSendHapticData.m b/SmartDeviceLink/SDLSendHapticData.m new file mode 100644 index 000000000..d1f85988a --- /dev/null +++ b/SmartDeviceLink/SDLSendHapticData.m @@ -0,0 +1,44 @@ +// +// SDLSendHapticData.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/3/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import "NSMutableDictionary+Store.h" +#import "SDLNames.h" +#import "SDLSendHapticData.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation SDLSendHapticData + +- (instancetype)init { + if (self = [super initWithName:SDLNameSendHapticData]) { + } + return self; +} + +- (instancetype)initWithHapticRectData:(NSArray<SDLHapticRect *> *)hapticRectData { + self = [self init]; + if (!self) { + return nil; + } + + self.hapticRectData = [hapticRectData mutableCopy]; + + return self; +} + +- (void)setHapticRectData:(nullable NSArray<SDLHapticRect *> *)hapticRectData { + [parameters sdl_setObject:hapticRectData forName:SDLNameHapticRectData]; +} + +- (nullable NSArray<SDLHapticRect *> *)hapticRectData { + return [parameters sdl_objectForName:SDLNameHapticRectData]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSendHapticDataResponse.h b/SmartDeviceLink/SDLSendHapticDataResponse.h new file mode 100644 index 000000000..c811c0e22 --- /dev/null +++ b/SmartDeviceLink/SDLSendHapticDataResponse.h @@ -0,0 +1,19 @@ +// +// SDLSendHapticDataResponse.h +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/4/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import "SDLRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN +/** + * SDLSendHapticDataResponse is sent when SDLSendHapticData has been called + */ +@interface SDLSendHapticDataResponse : SDLRPCResponse + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLSendHapticDataResponse.m b/SmartDeviceLink/SDLSendHapticDataResponse.m new file mode 100644 index 000000000..b1fa03607 --- /dev/null +++ b/SmartDeviceLink/SDLSendHapticDataResponse.m @@ -0,0 +1,25 @@ + +// +// SDLSendHapticDataResponse.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/4/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import "SDLNames.h" +#import "SDLSendHapticDataResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation SDLSendHapticDataResponse + +- (instancetype)init { + if (self = [super initWithName:SDLNameSendHapticDataResponse]) { + } + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.h b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.h index d1b0e446a..1dcbcd09f 100644 --- a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.h +++ b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.h @@ -146,6 +146,16 @@ extern SDLAudioStreamState *const SDLAudioStreamStateShuttingDown; - (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer; /** + * This method receives raw image data and will run iOS8+'s hardware video encoder to turn the data into a video stream, which will then be passed to the connected head unit. + * + * @param imageBuffer A CVImageBufferRef to be encoded by Video Toolbox + * @param presentationTimestamp A presentation timestamp for the frame, or kCMTimeInvalid if timestamp is unknown. If it's valid, it must be greater than the previous one. + * + * @return Whether or not the data was successfully encoded and sent. + */ +- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp; + +/** * This method receives PCM audio data and will attempt to send that data across to the head unit for immediate playback * * @param audioData The data in PCM audio format, to be played diff --git a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m index b56313eba..53a813551 100644 --- a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m +++ b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m @@ -64,6 +64,8 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; @property (assign, nonatomic) CV_NULLABLE CVPixelBufferRef backgroundingPixelBuffer; +@property (assign, nonatomic) CMTime lastPresentationTimestamp; + @end @@ -114,6 +116,8 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_appStateDidUpdate:) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_appStateDidUpdate:) name:UIApplicationWillResignActiveNotification object:nil]; + _lastPresentationTimestamp = kCMTimeInvalid; + return self; } @@ -136,6 +140,10 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; } - (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer { + return [self sendVideoData:imageBuffer presentationTimestamp:kCMTimeInvalid]; +} + +- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp { if (!self.isVideoConnected) { SDLLogW(@"Attempted to send video data, but not connected"); return NO; @@ -146,8 +154,20 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; SDLLogW(@"Attempted to send video data, but the app is not in LIMITED or FULL HMI state"); return NO; } - - return [self.videoEncoder encodeFrame:imageBuffer]; + + /* + * reject input image for following cases: + * - presentation timestamp is not increasing + * - app tries to send images while background images are shown + */ + if (CMTIME_IS_VALID(self.lastPresentationTimestamp) && CMTIME_IS_VALID(presentationTimestamp) + && CMTIME_COMPARE_INLINE(presentationTimestamp, <=, self.lastPresentationTimestamp)) { + SDLLogW(@"The video data is out of date"); + return NO; + } + self.lastPresentationTimestamp = presentationTimestamp; + + return [self.videoEncoder encodeFrame:imageBuffer presentationTimestamp:presentationTimestamp]; } - (BOOL)sendAudioData:(NSData*)audioData { @@ -297,6 +317,7 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; self.backgroundingPixelBuffer = backgroundingPixelBuffer; } + self.lastPresentationTimestamp = kCMTimeInvalid; } [[NSNotificationCenter defaultCenter] postNotificationName:SDLVideoStreamDidStartNotification object:nil]; @@ -517,8 +538,14 @@ static NSUInteger const SDLFramesToSendOnBackground = 30; return; } + const CMTime interval = CMTimeMake(1, 30); for (int frameCount = 0; frameCount < SDLFramesToSendOnBackground; frameCount++) { - [self.videoEncoder encodeFrame:self.backgroundingPixelBuffer]; + if (CMTIME_IS_VALID(self.lastPresentationTimestamp)) { + self.lastPresentationTimestamp = CMTimeAdd(self.lastPresentationTimestamp, interval); + [self.videoEncoder encodeFrame:self.backgroundingPixelBuffer presentationTimestamp:self.lastPresentationTimestamp]; + } else { + [self.videoEncoder encodeFrame:self.backgroundingPixelBuffer]; + } } } diff --git a/SmartDeviceLink/SDLStreamingMediaManager.h b/SmartDeviceLink/SDLStreamingMediaManager.h index 232856d40..46169484b 100644 --- a/SmartDeviceLink/SDLStreamingMediaManager.h +++ b/SmartDeviceLink/SDLStreamingMediaManager.h @@ -114,6 +114,16 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer; /** + * This method receives raw image data and will run iOS8+'s hardware video encoder to turn the data into a video stream, which will then be passed to the connected head unit. + * + * @param imageBuffer A CVImageBufferRef to be encoded by Video Toolbox + * @param presentationTimestamp A presentation timestamp for the frame, or kCMTimeInvalid if timestamp is unknown. If it's valid, it must be greater than the previous one. + * + * @return Whether or not the data was successfully encoded and sent. + */ +- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp; + +/** * This method receives PCM audio data and will attempt to send that data across to the head unit for immediate playback * * @param audioData The data in PCM audio format, to be played diff --git a/SmartDeviceLink/SDLStreamingMediaManager.m b/SmartDeviceLink/SDLStreamingMediaManager.m index 1c4291931..72bbf9691 100644 --- a/SmartDeviceLink/SDLStreamingMediaManager.m +++ b/SmartDeviceLink/SDLStreamingMediaManager.m @@ -53,6 +53,10 @@ NS_ASSUME_NONNULL_BEGIN return [self.lifecycleManager sendVideoData:imageBuffer]; } +- (BOOL)sendVideoData:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp { + return [self.lifecycleManager sendVideoData:imageBuffer presentationTimestamp:presentationTimestamp]; +} + - (BOOL)sendAudioData:(NSData*)audioData { return [self.lifecycleManager sendAudioData:audioData]; } diff --git a/SmartDeviceLink/SDLVideoEncoder.h b/SmartDeviceLink/SDLVideoEncoder.h index e48f8c311..53816452b 100644 --- a/SmartDeviceLink/SDLVideoEncoder.h +++ b/SmartDeviceLink/SDLVideoEncoder.h @@ -60,6 +60,8 @@ extern NSString *const SDLErrorDomainVideoEncoder; - (BOOL)encodeFrame:(CVImageBufferRef)imageBuffer; +- (BOOL)encodeFrame:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp; + /** * Creates a new pixel buffer using the pixelBufferPool property. */ diff --git a/SmartDeviceLink/SDLVideoEncoder.m b/SmartDeviceLink/SDLVideoEncoder.m index 91eed8631..f3216d851 100644 --- a/SmartDeviceLink/SDLVideoEncoder.m +++ b/SmartDeviceLink/SDLVideoEncoder.m @@ -8,6 +8,7 @@ #import "SDLVideoEncoder.h" +#import "SDLH264ByteStreamPacketizer.h" #import "SDLLogMacros.h" @@ -22,6 +23,8 @@ static NSDictionary<NSString *, id>* _defaultVideoEncoderSettings; @property (assign, nonatomic, nullable) VTCompressionSessionRef compressionSession; @property (assign, nonatomic, nullable) CFDictionaryRef sdl_pixelBufferOptions; @property (assign, nonatomic) NSUInteger currentFrameNumber; +@property (nonatomic) id<SDLH264Packetizer> packetizer; +@property (assign, nonatomic) double timestampOffset; @end @@ -106,7 +109,10 @@ static NSDictionary<NSString *, id>* _defaultVideoEncoderSettings; return nil; } } - + + _packetizer = [[SDLH264ByteStreamPacketizer alloc] init]; + _timestampOffset = 0.0; + return self; } @@ -119,7 +125,16 @@ static NSDictionary<NSString *, id>* _defaultVideoEncoderSettings; } - (BOOL)encodeFrame:(CVImageBufferRef)imageBuffer { - OSStatus status = VTCompressionSessionEncodeFrame(_compressionSession, imageBuffer, CMTimeMake(self.currentFrameNumber++, 30), kCMTimeInvalid, NULL, (__bridge void *)self, NULL); + return [self encodeFrame:imageBuffer presentationTimestamp:kCMTimeInvalid]; +} + +- (BOOL)encodeFrame:(CVImageBufferRef)imageBuffer presentationTimestamp:(CMTime)presentationTimestamp { + if (!CMTIME_IS_VALID(presentationTimestamp)) { + presentationTimestamp = CMTimeMake(self.currentFrameNumber, 30); + } + self.currentFrameNumber++; + + OSStatus status = VTCompressionSessionEncodeFrame(_compressionSession, imageBuffer, presentationTimestamp, kCMTimeInvalid, NULL, (__bridge void *)self, NULL); return (status == noErr); } @@ -161,10 +176,25 @@ void sdl_videoEncoderOutputCallback(void * CM_NULLABLE outputCallbackRefCon, voi } SDLVideoEncoder *encoder = (__bridge SDLVideoEncoder *)sourceFrameRefCon; - NSData *elementaryStreamData = [encoder.class sdl_encodeElementaryStreamWithSampleBuffer:sampleBuffer]; + NSArray *nalUnits = [encoder.class sdl_extractNalUnitsFromSampleBuffer:sampleBuffer]; + + const CMTime presentationTimestampInCMTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); + double presentationTimestamp = 0.0; + if (CMTIME_IS_VALID(presentationTimestampInCMTime)) { + presentationTimestamp = CMTimeGetSeconds(presentationTimestampInCMTime); + } + if (encoder.timestampOffset == 0.0) { + // remember this first timestamp as the offset + encoder.timestampOffset = presentationTimestamp; + } + + NSArray *packets = [encoder.packetizer createPackets:nalUnits + presentationTimestamp:(presentationTimestamp - encoder.timestampOffset)]; if ([encoder.delegate respondsToSelector:@selector(videoEncoder:hasEncodedFrame:)]) { - [encoder.delegate videoEncoder:encoder hasEncodedFrame:elementaryStreamData]; + for (NSData *packet in packets) { + [encoder.delegate videoEncoder:encoder hasEncodedFrame:packet]; + } } } @@ -190,9 +220,9 @@ void sdl_videoEncoderOutputCallback(void * CM_NULLABLE outputCallbackRefCon, voi } #pragma mark Helpers -+ (NSData *)sdl_encodeElementaryStreamWithSampleBuffer:(CMSampleBufferRef)sampleBuffer { ++ (NSArray *)sdl_extractNalUnitsFromSampleBuffer:(CMSampleBufferRef)sampleBuffer { // Creating an elementaryStream: http://stackoverflow.com/questions/28396622/extracting-h264-from-cmblockbuffer - NSMutableData *elementaryStream = [NSMutableData data]; + NSMutableArray *nalUnits = [NSMutableArray array]; BOOL isIFrame = NO; CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, 0); @@ -205,10 +235,6 @@ void sdl_videoEncoderOutputCallback(void * CM_NULLABLE outputCallbackRefCon, voi isIFrame = !keyExists || !CFBooleanGetValue(notSync); } - // This is the start code that we will write to the elementary stream before every NAL unit - static const size_t startCodeLength = 4; - static const uint8_t startCode[] = {0x00, 0x00, 0x00, 0x01}; - // Write the SPS and PPS NAL units to the elementary stream before every I-Frame if (isIFrame) { CMFormatDescriptionRef description = CMSampleBufferGetFormatDescription(sampleBuffer); @@ -233,9 +259,9 @@ void sdl_videoEncoderOutputCallback(void * CM_NULLABLE outputCallbackRefCon, voi NULL, NULL); - // Write the parameter set to the elementary stream - [elementaryStream appendBytes:startCode length:startCodeLength]; - [elementaryStream appendBytes:parameterSetPointer length:parameterSetLength]; + // Output the parameter set + NSData *nalUnit = [NSData dataWithBytesNoCopy:(uint8_t *)parameterSetPointer length:parameterSetLength freeWhenDone:NO]; + [nalUnits addObject:nalUnit]; } } @@ -256,17 +282,17 @@ void sdl_videoEncoderOutputCallback(void * CM_NULLABLE outputCallbackRefCon, voi // Convert the length value from Big-endian to Little-endian NALUnitLength = CFSwapInt32BigToHost(NALUnitLength); - [elementaryStream appendBytes:startCode length:startCodeLength]; // Write the NAL unit without the AVCC length header to the elementary stream - [elementaryStream appendBytes:bufferDataPointer + bufferOffset + AVCCHeaderLength length:NALUnitLength]; + NSData *nalUnit = [NSData dataWithBytesNoCopy:bufferDataPointer + bufferOffset + AVCCHeaderLength length:NALUnitLength freeWhenDone:NO]; + [nalUnits addObject:nalUnit]; // Move to the next NAL unit in the block buffer bufferOffset += AVCCHeaderLength + NALUnitLength; } - return elementaryStream; + return nalUnits; } @end diff --git a/SmartDeviceLink/SDLVideoStreamingCapability.h b/SmartDeviceLink/SDLVideoStreamingCapability.h index a19ec1a68..86de1f1ac 100644 --- a/SmartDeviceLink/SDLVideoStreamingCapability.h +++ b/SmartDeviceLink/SDLVideoStreamingCapability.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SDLVideoStreamingCapability : SDLRPCStruct -- (instancetype)initWithVideoStreaming:(nullable SDLImageResolution *)preferredResolution maxBitrate:(nullable NSNumber *)maxBitrate supportedFormats:(nullable NSArray<SDLVideoStreamingFormat *> *)supportedFormats; +- (instancetype)initWithVideoStreaming:(nullable SDLImageResolution *)preferredResolution maxBitrate:(nullable NSNumber<SDLInt> *)maxBitrate supportedFormats:(nullable NSArray<SDLVideoStreamingFormat *> *)supportedFormats hapticDataSupported:(nullable NSNumber<SDLBool> *)hapticDataSupported; /** * @abstract The preferred resolution of a video stream for decoding and rendering on HMI, optional */ @@ -35,6 +35,10 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nullable, strong, nonatomic) NSArray<SDLVideoStreamingFormat *> *supportedFormats; +/** + True if the system can utilize the haptic spatial data from the source being streamed. + */ +@property (nullable, strong, nonatomic) NSNumber<SDLBool> *hapticSpatialDataSupported; @end diff --git a/SmartDeviceLink/SDLVideoStreamingCapability.m b/SmartDeviceLink/SDLVideoStreamingCapability.m index cf4d33e98..75873a710 100644 --- a/SmartDeviceLink/SDLVideoStreamingCapability.m +++ b/SmartDeviceLink/SDLVideoStreamingCapability.m @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation SDLVideoStreamingCapability -- (instancetype)initWithVideoStreaming:(nullable SDLImageResolution *)preferredResolution maxBitrate:(nullable NSNumber *)maxBitrate supportedFormats:(nullable NSArray<SDLVideoStreamingFormat *> *)supportedFormats { +- (instancetype)initWithVideoStreaming:(nullable SDLImageResolution *)preferredResolution maxBitrate:(nullable NSNumber<SDLInt> *)maxBitrate supportedFormats:(nullable NSArray<SDLVideoStreamingFormat *> *)supportedFormats hapticDataSupported:(nullable NSNumber<SDLBool> *)hapticDataSupported { self = [self init]; if (!self) { return self; @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN self.maxBitrate = maxBitrate; self.preferredResolution = preferredResolution; self.supportedFormats = supportedFormats; + self.hapticSpatialDataSupported = hapticDataSupported; return self; } @@ -54,6 +55,14 @@ NS_ASSUME_NONNULL_BEGIN return [store sdl_objectsForName:SDLNameSupportedFormats ofClass:SDLVideoStreamingFormat.class]; } +- (void)setHapticSpatialDataSupported:(nullable NSNumber<SDLBool> *)hapticSpatialDataSupported { + [store sdl_setObject:hapticSpatialDataSupported forName:SDLNameHapticSpatialDataSupported]; +} + +- (nullable NSNumber<SDLBool> *)hapticSpatialDataSupported { + return [store sdl_objectForName:SDLNameHapticSpatialDataSupported]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SmartDeviceLink/SmartDeviceLink.h b/SmartDeviceLink/SmartDeviceLink.h index 5ef93ec4a..3f72f59ff 100644 --- a/SmartDeviceLink/SmartDeviceLink.h +++ b/SmartDeviceLink/SmartDeviceLink.h @@ -72,6 +72,7 @@ FOUNDATION_EXPORT const unsigned char SmartDeviceLinkVersionString[]; #import "SDLRegisterAppInterface.h" #import "SDLResetGlobalProperties.h" #import "SDLScrollableMessage.h" +#import "SDLSendHapticData.h" #import "SDLSendLocation.h" #import "SDLSetAppIcon.h" #import "SDLSetDisplayLayout.h" @@ -119,6 +120,7 @@ FOUNDATION_EXPORT const unsigned char SmartDeviceLinkVersionString[]; #import "SDLRegisterAppInterfaceResponse.h" #import "SDLResetGlobalPropertiesResponse.h" #import "SDLScrollableMessageResponse.h" +#import "SDLSendHapticDataResponse.h" #import "SDLSendLocationResponse.h" #import "SDLSetAppIconResponse.h" #import "SDLSetDisplayLayoutResponse.h" @@ -176,6 +178,7 @@ FOUNDATION_EXPORT const unsigned char SmartDeviceLinkVersionString[]; #import "SDLECallInfo.h" #import "SDLEmergencyEvent.h" #import "SDLGPSData.h" +#import "SDLHapticRect.h" #import "SDLHMICapabilities.h" #import "SDLHMIPermissions.h" #import "SDLHeadLampStatus.h" @@ -194,6 +197,7 @@ FOUNDATION_EXPORT const unsigned char SmartDeviceLinkVersionString[]; #import "SDLPermissionItem.h" #import "SDLPhoneCapability.h" #import "SDLPresetBankCapabilities.h" +#import "SDLRectangle.h" #import "SDLScreenParams.h" #import "SDLSingleTireStatus.h" #import "SDLSoftButton.h" diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLH264ByteStreamPacketizerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLH264ByteStreamPacketizerSpec.m new file mode 100644 index 000000000..59a3529bf --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLH264ByteStreamPacketizerSpec.m @@ -0,0 +1,89 @@ +// +// SDLH264ByteStreamPacketizerSpec.m +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/13/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> +#import "SDLH264ByteStreamPacketizer.h" + +QuickSpecBegin(SDLH264ByteStreamPacketizerSpec) + +describe(@"a H264 byte stream packetizer", ^{ + // sample NAL units (SPS, PPS, I-frame, P-frame) + const UInt8 spsData[] = {0x67, 0x42, 0xC0, 0x0A, 0xA6, 0x11, 0x11, 0xE8, 0x40, 0x00, 0x00, 0xFA, 0x40, 0x00, 0x3A, 0x98, 0x23, 0xC4, 0x89, 0x84, 0x60}; + const UInt8 ppsData[] = {0x68, 0xC8, 0x42, 0x0F, 0x13, 0x20}; + const UInt8 iframeData[] = {0x65, 0x88, 0x82, 0x07, 0x67, 0x39, 0x31, 0x40, 0x00, 0x5E, 0x0A, 0xFB, 0xEF, 0xAE, 0xBA, 0xEB, 0xAE, 0xBA, 0xEB, 0xC0}; + const UInt8 pframeData[] = {0x41, 0x9A, 0x1C, 0x0E, 0xCE, 0x71, 0xB0}; + + NSData *sps = [NSData dataWithBytes:spsData length:sizeof(spsData)]; + NSData *pps = [NSData dataWithBytes:ppsData length:sizeof(ppsData)]; + NSData *iframe = [NSData dataWithBytes:iframeData length:sizeof(iframeData)]; + NSData *pframe = [NSData dataWithBytes:pframeData length:sizeof(pframeData)]; + + __block SDLH264ByteStreamPacketizer *packetizer = nil; + + beforeEach(^{ + packetizer = [[SDLH264ByteStreamPacketizer alloc] init]; + }); + + describe(@"its output array", ^{ + it(@"always has one packet", ^{ + NSArray *nalUnits1 = @[sps, pps, iframe]; + NSArray *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + expect(@(results.count)).to(equal(@1)); + + NSArray *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + expect(@(results.count)).to(equal(@1)); + }); + }); + + describe(@"its output packet", ^{ + it(@"starts with a start code of 0x00 0x00 0x00 0x01", ^{ + const UInt8 startCode[] = {0x00, 0x00, 0x00, 0x01}; + + NSArray<NSData *> *nalUnits = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + const UInt8 *p = results[0].bytes; + + int ret = memcmp(p, startCode, sizeof(startCode)); + expect(@(ret)).to(equal(@0)); + }); + + it(@"then duplicates original H.264 NAL unit", ^{ + NSData *nalUnit = iframe; + + NSArray<NSData *> *nalUnits = @[nalUnit]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + const UInt8 *p = results[0].bytes; + + int ret = memcmp(p + 4, nalUnit.bytes, nalUnit.length); + expect(@(ret)).to(equal(@0)); + }); + + it(@"repeats for all given NAL units", ^{ + const UInt8 startCode[] = {0x00, 0x00, 0x00, 0x01}; + + NSArray<NSData *> *nalUnits = @[sps, pps, iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + const UInt8 *p = results[0].bytes; + + for (NSData *nalUnit in nalUnits) { + int ret = memcmp(p, startCode, sizeof(startCode)); + expect(@(ret)).to(equal(@0)); + p += sizeof(startCode); + + ret = memcmp(p, nalUnit.bytes, nalUnit.length); + expect(@(ret)).to(equal(@0)); + p += nalUnit.length; + } + }); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLNotificationDispatcherSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLNotificationDispatcherSpec.m index 3dff5dc9e..7c39d7b46 100644 --- a/SmartDeviceLinkTests/DevAPISpecs/SDLNotificationDispatcherSpec.m +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLNotificationDispatcherSpec.m @@ -63,6 +63,7 @@ describe(@"a notification dispatcher", ^{ expect(@([testDispatcher respondsToSelector:@selector(onRegisterAppInterfaceResponse:)])).to(beTruthy()); expect(@([testDispatcher respondsToSelector:@selector(onResetGlobalPropertiesResponse:)])).to(beTruthy()); expect(@([testDispatcher respondsToSelector:@selector(onScrollableMessageResponse:)])).to(beTruthy()); + expect(@([testDispatcher respondsToSelector:@selector(onSendHapticDataResponse:)])).to(beTruthy()); expect(@([testDispatcher respondsToSelector:@selector(onSendLocationResponse:)])).to(beTruthy()); expect(@([testDispatcher respondsToSelector:@selector(onSetAppIconResponse:)])).to(beTruthy()); expect(@([testDispatcher respondsToSelector:@selector(onSetDisplayLayoutResponse:)])).to(beTruthy()); diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLRTPH264PacketizerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLRTPH264PacketizerSpec.m new file mode 100644 index 000000000..a82e2ef65 --- /dev/null +++ b/SmartDeviceLinkTests/DevAPISpecs/SDLRTPH264PacketizerSpec.m @@ -0,0 +1,401 @@ +// +// SDLRTPH264PacketizerSpec.m +// SmartDeviceLink-iOS +// +// Created by Sho Amano on 4/13/17. +// Copyright © 2017 Xevo Inc. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> +#import "SDLRTPH264Packetizer.h" + +// read 2-byte in network byte order and convert it to a UInt16 +static inline UInt16 sdl_readShortInNetworkByteOrder(const UInt8 *buffer) { + return (buffer[0] << 8) | buffer[1]; +} + +// read 4-byte in network byte order and convert it to a UInt32 +static inline UInt32 sdl_readLongInNetworkByteOrder(const UInt8 *buffer) { + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; +} + +QuickSpecBegin(SDLRTPH264PacketizerSpec) + +describe(@"a RTP H264 packetizer", ^{ + // sample NAL units (SPS, PPS, I-frame, P-frame) + const UInt8 spsData[] = {0x67, 0x42, 0xC0, 0x0A, 0xA6, 0x11, 0x11, 0xE8, 0x40, 0x00, 0x00, 0xFA, 0x40, 0x00, 0x3A, 0x98, 0x23, 0xC4, 0x89, 0x84, 0x60}; + const UInt8 ppsData[] = {0x68, 0xC8, 0x42, 0x0F, 0x13, 0x20}; + const UInt8 iframeData[] = {0x65, 0x88, 0x82, 0x07, 0x67, 0x39, 0x31, 0x40, 0x00, 0x5E, 0x0A, 0xFB, 0xEF, 0xAE, 0xBA, 0xEB, 0xAE, 0xBA, 0xEB, 0xC0}; + const UInt8 pframeData[] = {0x41, 0x9A, 0x1C, 0x0E, 0xCE, 0x71, 0xB0}; + + NSData *sps = [NSData dataWithBytes:spsData length:sizeof(spsData)]; + NSData *pps = [NSData dataWithBytes:ppsData length:sizeof(ppsData)]; + NSData *iframe = [NSData dataWithBytes:iframeData length:sizeof(iframeData)]; + NSData *pframe = [NSData dataWithBytes:pframeData length:sizeof(pframeData)]; + + const NSUInteger FrameLengthLen = 2; + const NSUInteger MaxRTPPacketSize = 65535; + const NSUInteger RTPHeaderLen = 12; + const UInt8 DefaultPayloadType = 96; + const UInt8 FragmentationUnitVersionA = 0x1C; + const NSUInteger ClockRate = 90000; + + __block SDLRTPH264Packetizer *packetizer = nil; + + beforeEach(^{ + packetizer = [[SDLRTPH264Packetizer alloc] init]; + }); + + describe(@"its output array", ^{ + it(@"has same number or more elements compared to the input NAL units", ^{ + NSArray<NSData *> *nalUnits = @[sps, pps, iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + expect(@(results.count)).to(beGreaterThanOrEqualTo(@(nalUnits.count))); + }); + }); + + describe(@"First two bytes of its output", ^{ + it(@"indicates the length of a RTP packet", ^{ + NSArray<NSData *> *nalUnits = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + UInt16 length = sdl_readShortInNetworkByteOrder(header); + expect(@((length))).to(equal(@(results[0].length - FrameLengthLen))); + }); + }); + + describe(@"header of the RTP packet", ^{ + __block const UInt8 *header; + + beforeEach(^{ + NSArray<NSData *> *nalUnits = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + header = results[0].bytes; + }); + + it(@"indicates version 2", ^{ + expect(@((header[FrameLengthLen] >> 6) & 3)).to(equal(@2)); + }); + it(@"indicates no padding", ^{ + expect(@((header[FrameLengthLen] >> 5) & 1)).to(equal(@0)); + }); + it(@"indicates no extension", ^{ + expect(@((header[FrameLengthLen] >> 4) & 1)).to(equal(@0)); + }); + it(@"indicates no CSRC", ^{ + expect(@(header[FrameLengthLen] & 0xF)).to(equal(@0)); + }); + }); + + describe(@"the marker bit in the header of the RTP packet", ^{ + context(@"when there is only one NAL unit input", ^{ + it(@"is always set", ^{ + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + expect(@((header[FrameLengthLen+1] >> 7) & 1)).to(equal(@1)); + + NSArray<NSData *> *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + header = results[0].bytes; + expect(@((header[FrameLengthLen+1] >> 7) & 1)).to(equal(@1)); + }); + }); + + context(@"when multiple NAL units are input for an access unit", ^{ + it(@"is set only for the last packet", ^{ + // 3 NAL units for a frame + NSArray<NSData *> *nalUnits1 = @[sps, pps, iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + + [results enumerateObjectsUsingBlock:^(NSData *packet, NSUInteger index, BOOL *stop) { + const UInt8 *header = packet.bytes; + if (index == results.count - 1) { + expect(@((header[FrameLengthLen+1] >> 7) & 1)).to(equal(@1)); + } else { + expect(@((header[FrameLengthLen+1] >> 7) & 1)).to(equal(@0)); + } + }]; + + // Only 1 NAL unit for the next frame + NSArray<NSData *> *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + const UInt8 *header = results[0].bytes; + expect(@((header[FrameLengthLen+1] >> 7) & 1)).to(equal(@1)); + }); + }); + }); + + describe(@"the payload type in the header of the RTP packet", ^{ + context(@"when it is not configured", ^{ + it(@"equals to 96", ^{ + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + expect(@(header[FrameLengthLen+1] & 0x7F)).to(equal(@(DefaultPayloadType))); + }); + }); + + context(@"when it is explicitly configured", ^{ + it(@"is same as the given number if the number is between 0 and 127", ^{ + UInt8 payloadType = 100; + packetizer.payloadType = payloadType; + + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + expect(@(header[FrameLengthLen+1] & 0x7F)).to(equal(@(payloadType))); + }); + + it(@"equals to 96 if the given number is out of range", ^{ + packetizer.payloadType = 200; + + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + expect(@(header[FrameLengthLen+1] & 0x7F)).to(equal(@(DefaultPayloadType))); + }); + }); + }); + + describe(@"the sequence number in the header of the RTP packet", ^{ + it(@"has an initial value of random number", ^{ + // no way to test a random number + }); + + it(@"increments by one for the next packet", ^{ + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + UInt16 sequenceNumber = sdl_readShortInNetworkByteOrder(&header[FrameLengthLen+2]); + + NSArray<NSData *> *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + header = results[0].bytes; + + sequenceNumber++; + expect(@(sequenceNumber)).to(equal(@(sdl_readShortInNetworkByteOrder(&header[FrameLengthLen+2])))); + }); + + it(@"wraps around after reaching 65535", ^{ + NSArray<NSData *> *nalUnits = @[iframe]; + UInt16 prevSequenceNumber = 0; + + for (NSUInteger i = 0; i <= 65536; i++) { + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:i/30.0]; + const UInt8 *header = results[0].bytes; + UInt16 sequenceNumber = sdl_readShortInNetworkByteOrder(&header[FrameLengthLen+2]); + + if (prevSequenceNumber == 65535) { + expect(@(sequenceNumber)).to(equal(@(0))); + break; // end testing + } else { + prevSequenceNumber = sequenceNumber; + } + } + }); + }); + + describe(@"the timestamp field in the header of the RTP packet", ^{ + it(@"has an initial value of random number", ^{ + // no way to test a random number + }); + + it(@"then increases in 90 kHz clock value", ^{ + NSArray<NSData *> *nalUnits = @[iframe]; + UInt32 initialPresentationTimestamp = 0; + + for (NSUInteger i = 0; i <= 100; i++) { + // the timestamp increases by 1/30 seconds + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:i/30.0]; + const UInt8 *header = results[0].bytes; + UInt32 presentationTimestamp = sdl_readLongInNetworkByteOrder(&header[FrameLengthLen+4]); + + if (i == 0) { + initialPresentationTimestamp = presentationTimestamp; + } else { + UInt32 expectedPresentationTimestamp = initialPresentationTimestamp + i / 30.0 * ClockRate; + // accept calculation error (+-1) + expect(@(presentationTimestamp)).to(beGreaterThanOrEqualTo(@(expectedPresentationTimestamp - 1))); + expect(@(presentationTimestamp)).to(beLessThanOrEqualTo(@(expectedPresentationTimestamp + 1))); + } + } + }); + }); + + describe(@"the SSRC field in the header of the RTP packet", ^{ + context(@"when it is not configured", ^{ + it(@"is a random number", ^{ + // No way to test a random number. We only check that it is shared among packets. + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + UInt32 ssrc = sdl_readLongInNetworkByteOrder(&header[FrameLengthLen+8]); + + NSArray<NSData *> *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + header = results[0].bytes; + UInt32 ssrc2 = sdl_readLongInNetworkByteOrder(&header[FrameLengthLen+8]); + + expect(@(ssrc)).to(equal(@(ssrc2))); + }); + }); + + context(@"when it is explicitly configured", ^{ + it(@"is same as the given number", ^{ + UInt32 expectedSSRC = 0xFEDCBA98; + packetizer.ssrc = expectedSSRC; + + NSArray<NSData *> *nalUnits1 = @[iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits1 presentationTimestamp:0.0]; + const UInt8 *header = results[0].bytes; + UInt32 ssrc = sdl_readLongInNetworkByteOrder(&header[FrameLengthLen+8]); + expect(@(ssrc)).to(equal(@(expectedSSRC))); + + NSArray<NSData *> *nalUnits2 = @[pframe]; + results = [packetizer createPackets:nalUnits2 presentationTimestamp:1.0/30]; + header = results[0].bytes; + ssrc = sdl_readLongInNetworkByteOrder(&header[FrameLengthLen+8]); + expect(@(ssrc)).to(equal(@(expectedSSRC))); + }); + }); + }); + + describe(@"the payload of its output packet", ^{ + NSData *(^createFakeNALUnit)(UInt8, NSUInteger) = ^NSData *(UInt8 firstByte, NSUInteger length) { + UInt8 *data = malloc(length); + data[0] = firstByte; + for (NSUInteger i = 1; i < length; i++) { + data[i] = i % 256; + } + return [NSData dataWithBytes:data length:length]; + }; + + UInt8 firstByte; + [iframe getBytes:&firstByte length:1]; + + it(@"is not fragmented if input NAL unit size is less than 65524 bytes (65536-12)", ^{ + NSData *fakeNALUnit = createFakeNALUnit(firstByte, MaxRTPPacketSize - RTPHeaderLen); + NSArray<NSData *> *nalUnits = @[fakeNALUnit]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + + // we should get only one packet + expect(@(results.count)).to(equal(@(1))); + }); + + it(@"is fragmented if input NAL unit size equals to or is greater than 65524 bytes", ^{ + NSData *fakeNALUnit = createFakeNALUnit(firstByte, MaxRTPPacketSize - RTPHeaderLen + 1); + NSArray<NSData *> *nalUnits = @[fakeNALUnit]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + + // the NAL unit should be fragmented into two + expect(@(results.count)).to(equal(@(2))); + }); + + context(@"when payload is not fragmented", ^{ + it(@"is duplicate of input NAL unit", ^{ + NSArray<NSData *> *nalUnits = @[sps, pps, iframe]; + NSArray<NSData *> *results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + + NSUInteger nalUnitIndex = 0; + for (NSData *packet in results) { + const UInt8 *p = packet.bytes; + int ret = memcmp(p + FrameLengthLen + RTPHeaderLen, + nalUnits[nalUnitIndex].bytes, + nalUnits[nalUnitIndex].length); + expect(@(ret)).to(equal(@0)); + nalUnitIndex++; + } + }); + }); + + context(@"when payload is fragmented", ^{ + __block NSData *fakeNALUnit; + __block NSArray<NSData *> *results; + + beforeEach(^{ + fakeNALUnit = createFakeNALUnit(firstByte, MaxRTPPacketSize - RTPHeaderLen + 1); + NSArray<NSData *> *nalUnits = @[fakeNALUnit]; + results = [packetizer createPackets:nalUnits presentationTimestamp:0.0]; + }); + + describe(@"its first byte", ^{ + it(@"has F bit and NRI field which are same as those of the input NAL unit", ^{ + for (NSData *packet in results) { + const UInt8 *header = packet.bytes; + expect(@((header[FrameLengthLen+RTPHeaderLen] >> 5) & 3)).to(equal(@((firstByte >> 5) & 3))); + } + }); + + it(@"indicates a Fragmentation Unit Version A type (0x1C)", ^{ + for (NSData *packet in results) { + const UInt8 *header = packet.bytes; + expect(@(header[FrameLengthLen+RTPHeaderLen] & 0x1F)).to(equal(@(FragmentationUnitVersionA))); + } + }); + }); + + describe(@"its second byte", ^{ + it(@"has a start bit which is set only at the beginning of fragment group", ^{ + BOOL shouldBeFirstFragment = YES; + + for (NSUInteger i = 0; i < results.count; i++) { + const UInt8 *header = results[i].bytes; + UInt8 startBit = (header[FrameLengthLen+RTPHeaderLen+1] >> 7) & 1; + expect(@(startBit)).to(equal(@(shouldBeFirstFragment ? 1 : 0))); + shouldBeFirstFragment = NO; + } + }); + + it(@"has a end bit which is set only at the end of fragment group", ^{ + BOOL shouldBeLastFragment = NO; + + for (NSUInteger i = 0; i < results.count; i++) { + if (i == results.count - 1) { + shouldBeLastFragment = YES; + } else { + shouldBeLastFragment = NO; + } + + const UInt8 *header = results[i].bytes; + UInt8 endBit = (header[FrameLengthLen+RTPHeaderLen+1] >> 6) & 1; + expect(@(endBit)).to(equal(@(shouldBeLastFragment ? 1 : 0))); + } + }); + + it(@"has a reserved bit which is always zero", ^{ + for (NSUInteger i = 0; i < results.count; i++) { + const UInt8 *header = results[i].bytes; + expect(@((header[FrameLengthLen+RTPHeaderLen+1] >> 5) & 1)).to(equal(@(0))); + } + }); + + it(@"has a type field which is same as the input NAL unit's type", ^{ + for (NSUInteger i = 0; i < results.count; i++) { + const UInt8 *header = results[i].bytes; + expect(@(header[FrameLengthLen+RTPHeaderLen+1] & 0x1F)).to(equal(@(firstByte & 0x1F))); + } + }); + }); + + describe(@"its third and onward bytes", ^{ + it(@"equals to original NAL unit's second and onward bytes when concatenated", ^{ + NSMutableData *concatData = [[NSMutableData alloc] init]; + + for (NSUInteger i = 0; i < results.count; i++) { + NSData *packet = results[i]; + const UInt8 *p = packet.bytes; + [concatData appendBytes:p + FrameLengthLen + RTPHeaderLen + 2 + length:packet.length - (FrameLengthLen + RTPHeaderLen + 2)]; + } + + expect(@([concatData isEqualToData:[fakeNALUnit subdataWithRange:NSMakeRange(1, fakeNALUnit.length - 1)]])).to(beTruthy()); + }); + }); + }); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadNakSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadNakSpec.m index ef0d8fae2..df7bd1305 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadNakSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadNakSpec.m @@ -54,4 +54,18 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadNak *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadNak alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.rejectedParams).to(beNil()); + }); +}); + + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadVideoStartServiceSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadVideoStartServiceSpec.m index 1af113777..c93d72c9b 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadVideoStartServiceSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFramePayloadVideoStartServiceSpec.m @@ -77,4 +77,17 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadVideoStartService *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadVideoStartService alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFrameVideoStartServiceAckSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFrameVideoStartServiceAckSpec.m index cc7db881c..c817f44f4 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFrameVideoStartServiceAckSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/ControlFramePayloadSpecs/SDLControlFrameVideoStartServiceAckSpec.m @@ -83,4 +83,17 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadVideoStartServiceAck *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadVideoStartServiceAck alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadAudioStartServiceAckSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadAudioStartServiceAckSpec.m index a168fc39d..548110d0d 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadAudioStartServiceAckSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadAudioStartServiceAckSpec.m @@ -57,4 +57,18 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadAudioStartServiceAck *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadAudioStartServiceAck alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.mtu).to(equal(-1)); + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadEndServiceSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadEndServiceSpec.m index 8f7a0c3d2..a59c95811 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadEndServiceSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadEndServiceSpec.m @@ -55,4 +55,18 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadEndService *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadEndService alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.hashId).to(equal(-1)); + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceAckSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceAckSpec.m index ee6793249..7397f5713 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceAckSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceAckSpec.m @@ -68,4 +68,17 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadRPCStartServiceAck *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadRPCStartServiceAck alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceSpec.m index e8e773c8a..f39204330 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLControlFramePayloadRPCStartServiceSpec.m @@ -57,4 +57,18 @@ describe(@"Test decoding data", ^{ }); }); +describe(@"Test nil data", ^{ + __block SDLControlFramePayloadRPCStartService *testPayload = nil; + __block NSData *testData = nil; + + beforeEach(^{ + testPayload = [[SDLControlFramePayloadRPCStartService alloc] initWithData:testData]; + }); + + it(@"should output the correct params", ^{ + expect(testPayload.protocolVersion).to(beNil()); + expect(testPayload.data.length).to(equal(0)); + }); +}); + QuickSpecEnd diff --git a/SmartDeviceLinkTests/ProtocolSpecs/SDLFunctionIDSpec.m b/SmartDeviceLinkTests/ProtocolSpecs/SDLFunctionIDSpec.m index b1f9965fb..51145a6f6 100644 --- a/SmartDeviceLinkTests/ProtocolSpecs/SDLFunctionIDSpec.m +++ b/SmartDeviceLinkTests/ProtocolSpecs/SDLFunctionIDSpec.m @@ -60,6 +60,8 @@ describe(@"GetFunctionName Tests", ^ { expect([functionID functionNameForId:45]).to(equal(SDLNameGetWayPoints)); expect([functionID functionNameForId:46]).to(equal(SDLNameSubscribeWayPoints)); expect([functionID functionNameForId:47]).to(equal(SDLNameUnsubscribeWayPoints)); + expect([functionID functionNameForId:48]).to(equal(SDLNameGetSystemCapability)); + expect([functionID functionNameForId:49]).to(equal(SDLNameSendHapticData)); expect([functionID functionNameForId:32768]).to(equal(SDLNameOnHMIStatus)); expect([functionID functionNameForId:32769]).to(equal(SDLNameOnAppInterfaceUnregistered)); expect([functionID functionNameForId:32770]).to(equal(SDLNameOnButtonEvent)); @@ -131,6 +133,8 @@ describe(@"GetFunctionID Tests", ^ { expect([functionID functionIdForName:SDLNameGetWayPoints]).to(equal(@45)); expect([functionID functionIdForName:SDLNameSubscribeWayPoints]).to(equal(@46)); expect([functionID functionIdForName:SDLNameUnsubscribeWayPoints]).to(equal(@47)); + expect([functionID functionIdForName:SDLNameGetSystemCapability]).to(equal(@48)); + expect([functionID functionIdForName:SDLNameSendHapticData]).to(equal(@49)); expect([functionID functionIdForName:SDLNameOnHMIStatus]).to(equal(@32768)); expect([functionID functionIdForName:SDLNameOnAppInterfaceUnregistered]).to(equal(@32769)); expect([functionID functionIdForName:SDLNameOnButtonEvent]).to(equal(@32770)); diff --git a/SmartDeviceLinkTests/RPCSpecs/EnumSpecs/SDLMetadataTypeSpec.m b/SmartDeviceLinkTests/RPCSpecs/EnumSpecs/SDLMetadataTypeSpec.m index 1ede591ee..0ef9bc45f 100644 --- a/SmartDeviceLinkTests/RPCSpecs/EnumSpecs/SDLMetadataTypeSpec.m +++ b/SmartDeviceLinkTests/RPCSpecs/EnumSpecs/SDLMetadataTypeSpec.m @@ -29,7 +29,6 @@ describe(@"Individual Enum Value Tests", ^ { expect(SDLMetadataTypeMinimumTemperature).to(equal(@"minimumTemperature")); expect(SDLMetadataTypeWeatherTerm).to(equal(@"weatherTerm")); expect(SDLMetadataTypeHumidity).to(equal(@"humidity")); - expect(SDLMetadataTypeNone).to(equal(@"none")); }); }); diff --git a/SmartDeviceLinkTests/RPCSpecs/RequestSpecs/SDLSendHapticDataSpec.m b/SmartDeviceLinkTests/RPCSpecs/RequestSpecs/SDLSendHapticDataSpec.m new file mode 100644 index 000000000..24bf7ddb2 --- /dev/null +++ b/SmartDeviceLinkTests/RPCSpecs/RequestSpecs/SDLSendHapticDataSpec.m @@ -0,0 +1,62 @@ +// +// SDLSendHapticDataSpec.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/4/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> + +#import "SDLNames.h" +#import "SDLHapticRect.h" +#import "SDLRectangle.h" +#import "SDLSendHapticData.h" + +QuickSpecBegin(SDLSendHapticDataSpec) + +describe(@"Initialization Tests", ^ { + __block SDLHapticRect *testStruct = nil; + + beforeEach(^{ + testStruct = [[SDLHapticRect alloc] initWithId:123 rect:[[SDLRectangle alloc] initWithX:23.1 y:45.6 width:69.0 height:69]]; + }); + + context(@"Getter/Setter Tests", ^ { + it(@"Should set and get correctly", ^ { + SDLSendHapticData *testRequest = [[SDLSendHapticData alloc] init]; + testRequest.hapticRectData = [@[testStruct] mutableCopy]; + + expect(testRequest.hapticRectData).to(equal(@[testStruct])); + }); + }); + + context(@"Init tests", ^{ + it(@"Should get correctly when initialized with a dictionary", ^ { + NSMutableDictionary* dict = [@{SDLNameRequest: + @{SDLNameParameters: + @{SDLNameHapticRectData:@[testStruct]}, + SDLNameOperationName:SDLNameSendHapticData}} mutableCopy]; + SDLSendHapticData *testRequest = [[SDLSendHapticData alloc] initWithDictionary:dict]; + + expect(testRequest.hapticRectData).to(equal(@[testStruct])); + }); + + it(@"Should initialize correctly with initWithType:", ^{ + SDLSendHapticData *testRequest = [[SDLSendHapticData alloc] initWithHapticRectData:@[testStruct]]; + + expect(testRequest.hapticRectData).to(equal(@[testStruct])); + }); + + it(@"Should return nil if not set", ^ { + SDLSendHapticData *testRequest = [[SDLSendHapticData alloc] init]; + + expect(testRequest.hapticRectData).to(beNil()); + }); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/RPCSpecs/ResponseSpecs/SDLSendHapticDataResponseSpec.m b/SmartDeviceLinkTests/RPCSpecs/ResponseSpecs/SDLSendHapticDataResponseSpec.m new file mode 100644 index 000000000..29be138ad --- /dev/null +++ b/SmartDeviceLinkTests/RPCSpecs/ResponseSpecs/SDLSendHapticDataResponseSpec.m @@ -0,0 +1,19 @@ +// +// SDLSendHapticDataResponseSpec.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/4/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> + +#import "SDLNames.h" +#import "SDLSendHapticDataResponse.h" + +QuickSpecBegin(SDLSendHapticDataResponseSpec) + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLHapticRectSpec.m b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLHapticRectSpec.m new file mode 100644 index 000000000..6235ea62f --- /dev/null +++ b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLHapticRectSpec.m @@ -0,0 +1,69 @@ +// +// SDLHapticRectSpec.m +// SmartDeviceLink-iOS +// +// Created by Nicole on 8/3/17. +// Copyright © 2017 smartdevicelink. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> + +#import "SDLNames.h" +#import "SDLHapticRect.h" +#import "SDLRectangle.h" + +QuickSpecBegin(SDLHapticRectSpec) + +describe(@"Getter/Setter Tests", ^{ + __block SDLRectangle *testRect = nil; + beforeEach(^{ + testRect = [[SDLRectangle alloc] initWithX:1.2 y:42.3 width:78.9 height:69]; + }); + + it(@"Should set and get correctly", ^{ + SDLHapticRect *testStruct = [[SDLHapticRect alloc] init]; + + testStruct.id = @1; + testStruct.rect = testRect; + + expect(testStruct.id).to(equal(@1)); + expect(testStruct.rect).to(equal(testRect)); + }); + + it(@"Should get correctly when initialized with parameters", ^{ + SDLHapticRect *testStruct = [[SDLHapticRect alloc] initWithId:23 rect:testRect]; + + expect(testStruct.id).to(equal(@23)); + expect(testStruct.rect).to(equal(testRect)); + }); + + it(@"Should get correctly when initialized with a dict", ^{ + NSMutableDictionary *dict = [@{SDLNameId:@2, + SDLNameRect: @{ + SDLNameX:@20, + SDLNameY:@200, + SDLNameWidth:@2000, + SDLNameHeight:@3000 + } + } mutableCopy]; + SDLHapticRect *testStruct = [[SDLHapticRect alloc] initWithDictionary:dict]; + + expect(testStruct.id).to(equal(@2)); + expect(testStruct.rect.x).to(equal(@20)); + expect(testStruct.rect.y).to(equal(@200)); + expect(testStruct.rect.width).to(equal(@2000)); + expect(testStruct.rect.height).to(equal(@3000)); + }); + + it(@"Should return nil if not set", ^{ + SDLHapticRect *testStruct = [[SDLHapticRect alloc] init]; + + expect(testStruct.id).to(beNil()); + expect(testStruct.rect).to(beNil()); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLRectangleSpec.m b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLRectangleSpec.m new file mode 100644 index 000000000..59749135d --- /dev/null +++ b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLRectangleSpec.m @@ -0,0 +1,58 @@ +#import <Foundation/Foundation.h> + +#import <Quick/Quick.h> +#import <Nimble/Nimble.h> + +#import "SDLNames.h" +#import "SDLRectangle.h" + +QuickSpecBegin(SDLRectangleSpec) + +describe(@"Getter/Setter Tests", ^{ + it(@"Should set and get correctly", ^{ + SDLRectangle *testStruct = [[SDLRectangle alloc] init]; + + testStruct.x = @10; + testStruct.y = @100; + testStruct.width = @1000; + testStruct.height = @2000; + + expect(testStruct.x).to(equal(@10)); + expect(testStruct.y).to(equal(@100)); + expect(testStruct.width).to(equal(@1000)); + expect(testStruct.height).to(equal(@2000)); + }); + + it(@"Should get correctly when initialized with parameters", ^{ + SDLRectangle *testStruct = [[SDLRectangle alloc] initWithX:50.5 y:60.2 width:500 height:600]; + + expect(testStruct.x).to(beCloseTo(@50.5)); + expect(testStruct.y).to(beCloseTo(@60.2)); + expect(testStruct.width).to(equal(@500.0)); + expect(testStruct.height).to(equal(@600.0)); + }); + + it(@"Should get correctly when initialized with a dict", ^{ + NSDictionary *dict = @{SDLNameX:@20, + SDLNameY:@200, + SDLNameWidth:@2000, + SDLNameHeight:@3000}; + SDLRectangle *testStruct = [[SDLRectangle alloc] initWithDictionary:dict]; + + expect(testStruct.x).to(equal(@20)); + expect(testStruct.y).to(equal(@200)); + expect(testStruct.width).to(equal(@2000)); + expect(testStruct.height).to(equal(@3000)); + }); + + it(@"Should return nil if not set", ^{ + SDLRectangle *testStruct = [[SDLRectangle alloc] init]; + + expect(testStruct.x).to(beNil()); + expect(testStruct.y).to(beNil()); + expect(testStruct.width).to(beNil()); + expect(testStruct.height).to(beNil()); + }); +}); + +QuickSpecEnd diff --git a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLSystemCapabilitySpec.m b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLSystemCapabilitySpec.m index 27a7f713b..36f3c0e8e 100644 --- a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLSystemCapabilitySpec.m +++ b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLSystemCapabilitySpec.m @@ -82,12 +82,12 @@ describe(@"Initialization tests", ^{ }); it(@"should initialize correctly with initWithVideoStreamingCapability:", ^{ - SDLImageResolution* resolution = [[SDLImageResolution alloc] init]; resolution.resolutionWidth = @600; resolution.resolutionHeight = @500; NSNumber *maxBitrate = @100; + NSNumber *hapticDataSupported = @YES; SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] init]; format1.codec = SDLVideoStreamingCodecH264; @@ -99,7 +99,7 @@ describe(@"Initialization tests", ^{ NSArray<SDLVideoStreamingFormat *> *formatArray = @[format1, format2]; - SDLVideoStreamingCapability *testVidStruct = [[SDLVideoStreamingCapability alloc] initWithVideoStreaming:resolution maxBitrate:maxBitrate supportedFormats:formatArray]; + SDLVideoStreamingCapability *testVidStruct = [[SDLVideoStreamingCapability alloc] initWithVideoStreaming:resolution maxBitrate:maxBitrate supportedFormats:formatArray hapticDataSupported:hapticDataSupported]; SDLSystemCapability *testStruct = [[SDLSystemCapability alloc] initWithVideoStreamingCapability:testVidStruct]; expect(testStruct.systemCapabilityType).to(equal(SDLSystemCapabilityTypeVideoStreaming)); diff --git a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLVideoStreamingCapabilitySpec.m b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLVideoStreamingCapabilitySpec.m index 1c1d4cee8..c977e0db7 100644 --- a/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLVideoStreamingCapabilitySpec.m +++ b/SmartDeviceLinkTests/RPCSpecs/StructSpecs/SDLVideoStreamingCapabilitySpec.m @@ -22,12 +22,12 @@ QuickSpecBegin(SDLVideoStreamingCapabilitySpec) describe(@"Initialization tests", ^{ it(@"Should get correctly when initialized with a dictionary", ^ { - SDLImageResolution* resolution = [[SDLImageResolution alloc] init]; resolution.resolutionWidth = @600; resolution.resolutionHeight = @500; NSNumber *maxBitrate = @100; + NSNumber *hapticDataSupported = @NO; SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] init]; format1.codec = SDLVideoStreamingCodecH264; @@ -41,13 +41,15 @@ describe(@"Initialization tests", ^{ NSMutableDictionary* dict = [@{SDLNamePreferredResolution: resolution, SDLNameMaxBitrate: maxBitrate, - SDLNameSupportedFormats: formatArray} mutableCopy]; + SDLNameSupportedFormats: formatArray, + SDLNameHapticSpatialDataSupported: hapticDataSupported} mutableCopy]; SDLVideoStreamingCapability* testStruct = [[SDLVideoStreamingCapability alloc] initWithDictionary:dict]; expect(testStruct.preferredResolution).to(equal(resolution)); expect(testStruct.maxBitrate).to(equal(maxBitrate)); expect(testStruct.supportedFormats).to(equal(formatArray)); + expect(testStruct.hapticSpatialDataSupported).to(equal(hapticDataSupported)); }); it(@"Should return nil if not set", ^ { @@ -59,12 +61,12 @@ describe(@"Initialization tests", ^{ }); it(@"Should initialize correctly with initWithVideoStreaming:(SDLImageResolution *)preferredResolution (NSNumber *)maxBitrate (NSArray<SDLVideoStreamingFormat *> *)suportedFormats", ^ { - SDLImageResolution* resolution = [[SDLImageResolution alloc] init]; resolution.resolutionWidth = @600; resolution.resolutionHeight = @500; NSNumber *maxBitrate = @100; + NSNumber *hapticDataSupported = @YES; SDLVideoStreamingFormat *format1 = [[SDLVideoStreamingFormat alloc] init]; format1.codec = SDLVideoStreamingCodecH264; @@ -76,11 +78,12 @@ describe(@"Initialization tests", ^{ NSArray<SDLVideoStreamingFormat *> *formatArray = @[format1, format2]; - SDLVideoStreamingCapability *testStruct = [[SDLVideoStreamingCapability alloc] initWithVideoStreaming:resolution maxBitrate:maxBitrate supportedFormats:formatArray]; + SDLVideoStreamingCapability *testStruct = [[SDLVideoStreamingCapability alloc] initWithVideoStreaming:resolution maxBitrate:maxBitrate supportedFormats:formatArray hapticDataSupported:hapticDataSupported]; expect(testStruct.preferredResolution).to(equal(resolution)); expect(testStruct.maxBitrate).to(equal(maxBitrate)); expect(testStruct.supportedFormats).to(equal(formatArray)); + expect(testStruct.hapticSpatialDataSupported).to(equal(hapticDataSupported)); }); }); diff --git a/SmartDeviceLink_Example/Classes/ProxyManager.m b/SmartDeviceLink_Example/Classes/ProxyManager.m index 0da979b4b..a992a043d 100644 --- a/SmartDeviceLink_Example/Classes/ProxyManager.m +++ b/SmartDeviceLink_Example/Classes/ProxyManager.m @@ -136,6 +136,13 @@ NS_ASSUME_NONNULL_BEGIN show.graphic = [self.class sdlex_mainGraphicImage]; [self.sdlManager sendRequest:show]; + + SDLHapticRect *hapticRect = [[SDLHapticRect alloc] initWithId:1 rect:[[SDLRectangle alloc] initWithX:12.34 y:42.3 width:69 height:69]]; + SDLSendHapticData *sendHaptic = [[SDLSendHapticData alloc] initWithHapticRectData:@[hapticRect]]; + + [self.sdlManager sendRequest:sendHaptic withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { + NSLog(@"STOP"); + }]; } - (void)sdlex_setupPermissionsCallbacks { |