From b1346ad93c2e8d4c197e041a37093daf2af47252 Mon Sep 17 00:00:00 2001 From: Xuan Liu Date: Mon, 20 Apr 2020 10:41:11 +0800 Subject: [PATCH 1/4] fix warning; add a convenient method clearTimer(); add video decoder and mac demo. --- .codecov.yml | 1 + TelloSwift/Tello.swift | 11 +- TelloVideoDecoder/.gitignore | 68 ++ TelloVideoDecoder/README.md | 57 ++ .../project.pbxproj | 612 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../TelloVideoDecoderMac/AppDelegate.swift | 26 + .../AppIcon.appiconset/Contents.json | 58 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/Main.storyboard | 718 ++++++++++++++++++ .../TelloVideoDecoderMac/Info.plist | 36 + .../TelloVideoDecoderMac.entitlements | 14 + .../TelloVideoDecoderMac/ViewController.swift | 136 ++++ .../TelloVideoDecoderMacTests/Info.plist | 22 + .../TelloVideoDecoderMacTests.swift | 34 + .../TelloVideoDecoderMacUITests/Info.plist | 22 + .../TelloVideoDecoderMacUITests.swift | 43 ++ TelloVideoDecoder/TelloVideoH264Decoder.swift | 261 +++++++ 21 files changed, 2157 insertions(+), 1 deletion(-) create mode 100644 TelloVideoDecoder/.gitignore create mode 100644 TelloVideoDecoder/README.md create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.pbxproj create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/contents.xcworkspacedata create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/AppDelegate.swift create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/Contents.json create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/Base.lproj/Main.storyboard create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/Info.plist create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements create mode 100644 TelloVideoDecoder/TelloVideoDecoderMac/ViewController.swift create mode 100644 TelloVideoDecoder/TelloVideoDecoderMacTests/Info.plist create mode 100644 TelloVideoDecoder/TelloVideoDecoderMacTests/TelloVideoDecoderMacTests.swift create mode 100644 TelloVideoDecoder/TelloVideoDecoderMacUITests/Info.plist create mode 100644 TelloVideoDecoder/TelloVideoDecoderMacUITests/TelloVideoDecoderMacUITests.swift create mode 100644 TelloVideoDecoder/TelloVideoH264Decoder.swift diff --git a/.codecov.yml b/.codecov.yml index 02cd4e8..bd41fb4 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,3 +1,4 @@ ignore: - "TelloSwiftTests" - "Package.swift" + - "TelloVideoDecoder" diff --git a/TelloSwift/Tello.swift b/TelloSwift/Tello.swift index a5b6829..093f06c 100644 --- a/TelloSwift/Tello.swift +++ b/TelloSwift/Tello.swift @@ -170,12 +170,21 @@ open class Tello { RunLoop.main.add(kaTimer!, forMode: .common) } } - + + /// Invalidate timer and set it to nil + /// /// Make sure you call this method on the same thread as keepAlive() public func invalidate() { kaTimer?.invalidate() kaTimer = nil } + + /// lazy name for invalidate() to clear timer + /// + /// Make sure you call this method on the same thread as keepAlive() + public func clearTimer() { + invalidate() + } /// Only used in unit tests for now. func cleanup() { diff --git a/TelloVideoDecoder/.gitignore b/TelloVideoDecoder/.gitignore new file mode 100644 index 0000000..b64d2fb --- /dev/null +++ b/TelloVideoDecoder/.gitignore @@ -0,0 +1,68 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +Packages/ +Package.pins +Package.resolved +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output diff --git a/TelloVideoDecoder/README.md b/TelloVideoDecoder/README.md new file mode 100644 index 0000000..37f6de2 --- /dev/null +++ b/TelloVideoDecoder/README.md @@ -0,0 +1,57 @@ +# TelloVideoH264Decoder +This contains a demo macOS app to illustrate how to use the TelloVideoH264Decoder + +The TelloVideoH264Decoder serves like a demo decoder that can meet common requirement + +I'm no expert on video decoding and I merely assemble code from different places and make it work. It could be a little buggy, and not world-class flexible for some configurations (e.g. `decodeFlags`) + +Therefore I will be grateful that if you can send me pull requests to make it better. + +## Get a valid NAL Unit +You need to first generate valid NALU either by yourself or by `getNalUnits()`/ `readNalUnits()`. + +`getNalUnits(streamBuffer: Array) -> NALUnit?` will get all valid NALU and abandon incomplete NALU (data before next start code) + +`readNalUnits(streamBuffer:inout Array) -> NALUnit?` will consume `streamBuffer`. To stop reading, call `stopProcessing()` + +## Get CMSampleBuffer from NALU +Get a CMSampleBuffer by `getCMSampleBuffer(from nalu: NALUnit)` + +## Get CVImageBuffer from CMSampleBuffer +Get a `CVImageBuffer` by calling `decompress()` + +>decompress(sampleBuffer: CMSampleBuffer, outputHandler: @escaping VTDecompressionOutputHandler) -> OSStatus + +This is a wrapper of `VTDecompressionSessionDecodeFrame(_:sampleBuffer:flags:infoFlagsOut:outputHandler:)` + +note that +>outputHandler cannot be called with a session created with a VTDecompressionOutputCallbackRecord. + +### what if I want to use VTDecompressionOutputCallbackRecord? +Grab the source code and modify `createVTDecompressionSession()` by commenting back +```swift +var outputCallback = VTDecompressionOutputCallbackRecord() +outputCallback.decompressionOutputCallback = decodeFrameCallback +outputCallback.decompressionOutputRefCon = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) +``` +and pass to `outputCallback: ` in `VTDecompressionSessionCreate()` + +## About CMSampleTimingInfo +I don't quite understand what each parameter means while creating the CMSampleTimingInfo. Even if I don't set it, the video streaming seems working well. Welcome any improvement and knowledge sharing. + +## About renderVideoStream() +>renderVideoStream(streamBuffer: inout Array, to videoLayer: AVSampleBufferDisplayLayer) + +This is a lazy method for people just want to display the video stream from Tello with `AVSampleBufferDisplayLayer` + +It simply keeps consuming `streamBuffer`, so you would want to put it into a `DispatchQueue` as it will block current thread, and be careful not causing write/read corruption. This also applies to `readNalUnits()`. + +## TelloVideoDecoderMac +This mac app contains similar code used in `TelloVideoH264Decoder`, just to demonstrate how to use it in different ways. + +`startHandling()` behaves like `renderVideoStream()`. You only need one of them in `viewDidAppear()`. + +Remember "get a valid NALU" is the first step, so `getNALUnit()` is similar to `getNalUnits()`/ `readNalUnits()`. + +## One more thing +For more information, refer WWDC 2014 Session 513 "Direct Access to Video Encoding and Decoding" diff --git a/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.pbxproj b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6bbd55e --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.pbxproj @@ -0,0 +1,612 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + B6508FF2244D3D0100728AD4 /* TelloSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6508FF1244D3D0100728AD4 /* TelloSwift.framework */; }; + B6508FF3244D3D0100728AD4 /* TelloSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B6508FF1244D3D0100728AD4 /* TelloSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + B6508FF6244D453C00728AD4 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = B6508FF5244D453C00728AD4 /* README.md */; }; + B6F45860244D39C300A94272 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F4585F244D39C300A94272 /* AppDelegate.swift */; }; + B6F45862244D39C300A94272 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F45861244D39C300A94272 /* ViewController.swift */; }; + B6F45864244D39C500A94272 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B6F45863244D39C500A94272 /* Assets.xcassets */; }; + B6F45867244D39C600A94272 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B6F45865244D39C600A94272 /* Main.storyboard */; }; + B6F45873244D39C600A94272 /* TelloVideoDecoderMacTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F45872244D39C600A94272 /* TelloVideoDecoderMacTests.swift */; }; + B6F4587E244D39C600A94272 /* TelloVideoDecoderMacUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F4587D244D39C600A94272 /* TelloVideoDecoderMacUITests.swift */; }; + B6F4588C244D3A1000A94272 /* TelloVideoH264Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6F4588B244D3A1000A94272 /* TelloVideoH264Decoder.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B6F4586F244D39C600A94272 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B6F45854244D39C300A94272 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B6F4585B244D39C300A94272; + remoteInfo = TelloVideoDecoderMac; + }; + B6F4587A244D39C600A94272 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B6F45854244D39C300A94272 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B6F4585B244D39C300A94272; + remoteInfo = TelloVideoDecoderMac; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + B6508FF4244D3D0100728AD4 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + B6508FF3244D3D0100728AD4 /* TelloSwift.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + B6508FF1244D3D0100728AD4 /* TelloSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TelloSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B6508FF5244D453C00728AD4 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + B6F4585C244D39C300A94272 /* TelloVideoDecoderMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TelloVideoDecoderMac.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B6F4585F244D39C300A94272 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + B6F45861244D39C300A94272 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + B6F45863244D39C500A94272 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B6F45866244D39C600A94272 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B6F45868244D39C600A94272 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B6F45869244D39C600A94272 /* TelloVideoDecoderMac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TelloVideoDecoderMac.entitlements; sourceTree = ""; }; + B6F4586E244D39C600A94272 /* TelloVideoDecoderMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TelloVideoDecoderMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B6F45872244D39C600A94272 /* TelloVideoDecoderMacTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelloVideoDecoderMacTests.swift; sourceTree = ""; }; + B6F45874244D39C600A94272 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B6F45879244D39C600A94272 /* TelloVideoDecoderMacUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TelloVideoDecoderMacUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B6F4587D244D39C600A94272 /* TelloVideoDecoderMacUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelloVideoDecoderMacUITests.swift; sourceTree = ""; }; + B6F4587F244D39C600A94272 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B6F4588B244D3A1000A94272 /* TelloVideoH264Decoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelloVideoH264Decoder.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B6F45859244D39C300A94272 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B6508FF2244D3D0100728AD4 /* TelloSwift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F4586B244D39C600A94272 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F45876244D39C600A94272 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B6508FF0244D3D0100728AD4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B6508FF1244D3D0100728AD4 /* TelloSwift.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B6F45853244D39C300A94272 = { + isa = PBXGroup; + children = ( + B6508FF5244D453C00728AD4 /* README.md */, + B6F4588B244D3A1000A94272 /* TelloVideoH264Decoder.swift */, + B6F4585E244D39C300A94272 /* TelloVideoDecoderMac */, + B6F45871244D39C600A94272 /* TelloVideoDecoderMacTests */, + B6F4587C244D39C600A94272 /* TelloVideoDecoderMacUITests */, + B6F4585D244D39C300A94272 /* Products */, + B6508FF0244D3D0100728AD4 /* Frameworks */, + ); + sourceTree = ""; + }; + B6F4585D244D39C300A94272 /* Products */ = { + isa = PBXGroup; + children = ( + B6F4585C244D39C300A94272 /* TelloVideoDecoderMac.app */, + B6F4586E244D39C600A94272 /* TelloVideoDecoderMacTests.xctest */, + B6F45879244D39C600A94272 /* TelloVideoDecoderMacUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + B6F4585E244D39C300A94272 /* TelloVideoDecoderMac */ = { + isa = PBXGroup; + children = ( + B6F4585F244D39C300A94272 /* AppDelegate.swift */, + B6F45861244D39C300A94272 /* ViewController.swift */, + B6F45863244D39C500A94272 /* Assets.xcassets */, + B6F45865244D39C600A94272 /* Main.storyboard */, + B6F45868244D39C600A94272 /* Info.plist */, + B6F45869244D39C600A94272 /* TelloVideoDecoderMac.entitlements */, + ); + path = TelloVideoDecoderMac; + sourceTree = ""; + }; + B6F45871244D39C600A94272 /* TelloVideoDecoderMacTests */ = { + isa = PBXGroup; + children = ( + B6F45872244D39C600A94272 /* TelloVideoDecoderMacTests.swift */, + B6F45874244D39C600A94272 /* Info.plist */, + ); + path = TelloVideoDecoderMacTests; + sourceTree = ""; + }; + B6F4587C244D39C600A94272 /* TelloVideoDecoderMacUITests */ = { + isa = PBXGroup; + children = ( + B6F4587D244D39C600A94272 /* TelloVideoDecoderMacUITests.swift */, + B6F4587F244D39C600A94272 /* Info.plist */, + ); + path = TelloVideoDecoderMacUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B6F4585B244D39C300A94272 /* TelloVideoDecoderMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = B6F45882244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMac" */; + buildPhases = ( + B6F45858244D39C300A94272 /* Sources */, + B6F45859244D39C300A94272 /* Frameworks */, + B6F4585A244D39C300A94272 /* Resources */, + B6508FF4244D3D0100728AD4 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TelloVideoDecoderMac; + productName = TelloVideoDecoderMac; + productReference = B6F4585C244D39C300A94272 /* TelloVideoDecoderMac.app */; + productType = "com.apple.product-type.application"; + }; + B6F4586D244D39C600A94272 /* TelloVideoDecoderMacTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B6F45885244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMacTests" */; + buildPhases = ( + B6F4586A244D39C600A94272 /* Sources */, + B6F4586B244D39C600A94272 /* Frameworks */, + B6F4586C244D39C600A94272 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B6F45870244D39C600A94272 /* PBXTargetDependency */, + ); + name = TelloVideoDecoderMacTests; + productName = TelloVideoDecoderMacTests; + productReference = B6F4586E244D39C600A94272 /* TelloVideoDecoderMacTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + B6F45878244D39C600A94272 /* TelloVideoDecoderMacUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B6F45888244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMacUITests" */; + buildPhases = ( + B6F45875244D39C600A94272 /* Sources */, + B6F45876244D39C600A94272 /* Frameworks */, + B6F45877244D39C600A94272 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B6F4587B244D39C600A94272 /* PBXTargetDependency */, + ); + name = TelloVideoDecoderMacUITests; + productName = TelloVideoDecoderMacUITests; + productReference = B6F45879244D39C600A94272 /* TelloVideoDecoderMacUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B6F45854244D39C300A94272 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1140; + ORGANIZATIONNAME = TelloSwift; + TargetAttributes = { + B6F4585B244D39C300A94272 = { + CreatedOnToolsVersion = 11.4.1; + }; + B6F4586D244D39C600A94272 = { + CreatedOnToolsVersion = 11.4.1; + TestTargetID = B6F4585B244D39C300A94272; + }; + B6F45878244D39C600A94272 = { + CreatedOnToolsVersion = 11.4.1; + TestTargetID = B6F4585B244D39C300A94272; + }; + }; + }; + buildConfigurationList = B6F45857244D39C300A94272 /* Build configuration list for PBXProject "TelloVideoDecoderMac" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B6F45853244D39C300A94272; + productRefGroup = B6F4585D244D39C300A94272 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B6F4585B244D39C300A94272 /* TelloVideoDecoderMac */, + B6F4586D244D39C600A94272 /* TelloVideoDecoderMacTests */, + B6F45878244D39C600A94272 /* TelloVideoDecoderMacUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B6F4585A244D39C300A94272 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B6508FF6244D453C00728AD4 /* README.md in Resources */, + B6F45864244D39C500A94272 /* Assets.xcassets in Resources */, + B6F45867244D39C600A94272 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F4586C244D39C600A94272 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F45877244D39C600A94272 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B6F45858244D39C300A94272 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B6F45862244D39C300A94272 /* ViewController.swift in Sources */, + B6F45860244D39C300A94272 /* AppDelegate.swift in Sources */, + B6F4588C244D3A1000A94272 /* TelloVideoH264Decoder.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F4586A244D39C600A94272 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B6F45873244D39C600A94272 /* TelloVideoDecoderMacTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6F45875244D39C600A94272 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B6F4587E244D39C600A94272 /* TelloVideoDecoderMacUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B6F45870244D39C600A94272 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B6F4585B244D39C300A94272 /* TelloVideoDecoderMac */; + targetProxy = B6F4586F244D39C600A94272 /* PBXContainerItemProxy */; + }; + B6F4587B244D39C600A94272 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B6F4585B244D39C300A94272 /* TelloVideoDecoderMac */; + targetProxy = B6F4587A244D39C600A94272 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + B6F45865244D39C600A94272 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B6F45866244D39C600A94272 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B6F45880244D39C600A94272 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + B6F45881244D39C600A94272 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + B6F45883244D39C600A94272 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMac/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + B6F45884244D39C600A94272 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMac/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMac; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + B6F45886244D39C600A94272 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMacTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMacTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TelloVideoDecoderMac.app/Contents/MacOS/TelloVideoDecoderMac"; + }; + name = Debug; + }; + B6F45887244D39C600A94272 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMacTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMacTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TelloVideoDecoderMac.app/Contents/MacOS/TelloVideoDecoderMac"; + }; + name = Release; + }; + B6F45889244D39C600A94272 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMacUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMacUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = TelloVideoDecoderMac; + }; + name = Debug; + }; + B6F4588A244D39C600A94272 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = TelloVideoDecoderMacUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.telloswift.TelloVideoDecoderMacUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = TelloVideoDecoderMac; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B6F45857244D39C300A94272 /* Build configuration list for PBXProject "TelloVideoDecoderMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B6F45880244D39C600A94272 /* Debug */, + B6F45881244D39C600A94272 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B6F45882244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B6F45883244D39C600A94272 /* Debug */, + B6F45884244D39C600A94272 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B6F45885244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMacTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B6F45886244D39C600A94272 /* Debug */, + B6F45887244D39C600A94272 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B6F45888244D39C600A94272 /* Build configuration list for PBXNativeTarget "TelloVideoDecoderMacUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B6F45889244D39C600A94272 /* Debug */, + B6F4588A244D39C600A94272 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B6F45854244D39C300A94272 /* Project object */; +} diff --git a/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1c0e9ae --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/contents.xcworkspacedata b/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c2a200b --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/AppDelegate.swift b/TelloVideoDecoder/TelloVideoDecoderMac/AppDelegate.swift new file mode 100644 index 0000000..974bbd7 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/AppDelegate.swift @@ -0,0 +1,26 @@ +// +// AppDelegate.swift +// TelloVideoDecoderMac +// +// Created by Xuan Liu on 2020/4/20. +// Copyright © 2020 TelloSwift. All rights reserved. +// + +import Cocoa + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + + + + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + + +} + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/AppIcon.appiconset/Contents.json b/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/Contents.json b/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/Base.lproj/Main.storyboard b/TelloVideoDecoder/TelloVideoDecoderMac/Base.lproj/Main.storyboard new file mode 100644 index 0000000..59abe57 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/Base.lproj/Main.storyboard @@ -0,0 +1,718 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/Info.plist b/TelloVideoDecoder/TelloVideoDecoderMac/Info.plist new file mode 100644 index 0000000..ae222b2 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2020 TelloSwift. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements b/TelloVideoDecoder/TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements new file mode 100644 index 0000000..40b639e --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/TelloVideoDecoderMac.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMac/ViewController.swift b/TelloVideoDecoder/TelloVideoDecoderMac/ViewController.swift new file mode 100644 index 0000000..d3c6ead --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMac/ViewController.swift @@ -0,0 +1,136 @@ +// +// ViewController.swift +// TelloVideoDecoderMac +// +// Created by Xuan Liu on 2019/12/20. +// Copyright © 2020 Xuan Liu. All rights reserved. +// + +import Cocoa +import AVFoundation +import VideoToolbox +import TelloSwift + + +class ViewController: NSViewController, TelloVideoSteam { + + var videoLayer: AVSampleBufferDisplayLayer? + + var streamBuffer = Array() + + let decoder = TelloVideoH264Decoder() + + var tello: Tello! + + let startCode: [UInt8] = [0,0,0,1] + + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + tello = Tello() + tello.videoDelegate = self + print("connected:", tello.activate()) + print("battery:", tello.battery) + tello.enable(video: true) + tello.keepAlive(every: 10) + + videoLayer = AVSampleBufferDisplayLayer() + if let layer = videoLayer { + layer.frame = CGRect(x: 0, y: 0, width: 1280, height: 720) + layer.videoGravity = AVLayerVideoGravity.resizeAspectFill + + let _CMTimebasePointer = UnsafeMutablePointer.allocate(capacity: 1) + let status = CMTimebaseCreateWithMasterClock( allocator: kCFAllocatorDefault, masterClock: CMClockGetHostTimeClock(), timebaseOut: _CMTimebasePointer ) + layer.controlTimebase = _CMTimebasePointer.pointee + + if let controlTimeBase = layer.controlTimebase, status == noErr { + CMTimebaseSetTime(controlTimeBase, time: CMTime.zero); + CMTimebaseSetRate(controlTimeBase, rate: 1.0); + } + + self.view.layer = layer + self.view.wantsLayer = true + layer.display() + } + } + + override func viewDidAppear() { + DispatchQueue.global().asyncAfter(deadline: DispatchTime.now().advanced(by: .seconds(2))) { [unowned self] in + // use either startHandle() or decoder.renderVideoStream(), + // they just behave in different ways, but eventually the same, giving more flexibility +// self.startHandle() + self.decoder.renderVideoStream(streamBuffer: &self.streamBuffer, to: self.videoLayer!) + } + } + + override func viewWillDisappear() { + tello.clearTimer() + tello.shutdown() + } + + func telloStream(receive frame: Data?) { + + if let frame = frame { + let packet = [UInt8](frame) + streamBuffer.append(contentsOf: packet) + } + } + + public typealias NALU = Array + func getNALUnit() -> NALU? { + + if streamBuffer.count == 0 { + return nil + } + + //make sure start with start code + if streamBuffer.count < 5 || Array(streamBuffer[0...3]) != startCode { + return nil + } + + //find second start code, so startIndex = 4 + var startIndex = 4 + + while true { + + while ((startIndex + 3) < streamBuffer.count) { + if Array(streamBuffer[startIndex...startIndex+3]) == startCode { + + let packet = Array(streamBuffer[0.. + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMacTests/TelloVideoDecoderMacTests.swift b/TelloVideoDecoder/TelloVideoDecoderMacTests/TelloVideoDecoderMacTests.swift new file mode 100644 index 0000000..685cd26 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMacTests/TelloVideoDecoderMacTests.swift @@ -0,0 +1,34 @@ +// +// TelloVideoDecoderMacTests.swift +// TelloVideoDecoderMacTests +// +// Created by Xuan Liu on 2020/4/20. +// Copyright © 2020 TelloSwift. All rights reserved. +// + +import XCTest +@testable import TelloVideoDecoderMac + +class TelloVideoDecoderMacTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/TelloVideoDecoder/TelloVideoDecoderMacUITests/Info.plist b/TelloVideoDecoder/TelloVideoDecoderMacUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMacUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/TelloVideoDecoder/TelloVideoDecoderMacUITests/TelloVideoDecoderMacUITests.swift b/TelloVideoDecoder/TelloVideoDecoderMacUITests/TelloVideoDecoderMacUITests.swift new file mode 100644 index 0000000..9e540cd --- /dev/null +++ b/TelloVideoDecoder/TelloVideoDecoderMacUITests/TelloVideoDecoderMacUITests.swift @@ -0,0 +1,43 @@ +// +// TelloVideoDecoderMacUITests.swift +// TelloVideoDecoderMacUITests +// +// Created by Xuan Liu on 2020/4/20. +// Copyright © 2020 TelloSwift. All rights reserved. +// + +import XCTest + +class TelloVideoDecoderMacUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { + XCUIApplication().launch() + } + } + } +} diff --git a/TelloVideoDecoder/TelloVideoH264Decoder.swift b/TelloVideoDecoder/TelloVideoH264Decoder.swift new file mode 100644 index 0000000..ce710c1 --- /dev/null +++ b/TelloVideoDecoder/TelloVideoH264Decoder.swift @@ -0,0 +1,261 @@ +// +// TelloVideoH264Decoder.swift +// +// Created by Xuan Liu on 2019/12/25. +// Copyright © 2020 Xuan Liu. All rights reserved. +// Licensed under Apache License 2.0 +// + +import Foundation +import VideoToolbox +import AVFoundation + +public typealias NALUnit = Array + +public class TelloVideoH264Decoder { + + let startCode: NALUnit = [0, 0, 0, 1] + + var formatDesc: CMVideoFormatDescription? + var vtdSession: VTDecompressionSession? + + var sps: NALUnit? + var pps: NALUnit? + + var stop = true + + + /// Decode a stream buffer which contains H264 raw bytes, enqueue the CMSampleBuffer to AVSampleBufferDisplayLayer you provided, and call setNeedsDisplay for you. + /// + /// You normally would want to call this function in a DispatchQueue, as it will block current thread, since it keeps consuming the stream buffer. + /// + /// - Parameters: + /// - streamBuffer: inout Array + /// - videoLayer: AVSampleBufferDisplayLayer + public func renderVideoStream(streamBuffer: inout Array, to videoLayer: AVSampleBufferDisplayLayer) { + stop = false + while let nalu = readNalUnits(streamBuffer: &streamBuffer) { + guard !stop else { break } + + if let sampleBuffer = getCMSampleBuffer(from: nalu) { + let attachments:CFArray? = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true) + if let attachmentArray = attachments { + let dic = unsafeBitCast(CFArrayGetValueAtIndex(attachmentArray, 0), to: CFMutableDictionary.self) + + CFDictionarySetValue(dic, + Unmanaged.passUnretained(kCMSampleAttachmentKey_DisplayImmediately).toOpaque(), + Unmanaged.passUnretained(kCFBooleanTrue).toOpaque()) + } + + videoLayer.enqueue(sampleBuffer) + DispatchQueue.main.async { videoLayer.needsDisplay() } + } + } + } + + /// Stop consuming stream buffer and clean up decoding resources + /// + /// This function will call cleanUp() to free resources + public func stopProcessing() { + stop = true + cleanUp() + } + + /// This is a wrapper of VTDecompressionSessionDecodeFrame(_:sampleBuffer:flags:infoFlagsOut:outputHandler:) + /// - Parameters: + /// - sampleBuffer: CMSampleBuffer + /// - outputHandler: @escaping VTDecompressionOutputHandler + /// - Returns: OSStatus + public func decompress(sampleBuffer: CMSampleBuffer, outputHandler: @escaping VTDecompressionOutputHandler) -> OSStatus { + guard let session = vtdSession else { return -1 } + return VTDecompressionSessionDecodeFrame(session, sampleBuffer: sampleBuffer, flags: [._EnableAsynchronousDecompression, ._EnableTemporalProcessing], infoFlagsOut: nil, outputHandler: outputHandler) + } + + /// Create a CMSampleBuffer from a NAL unit. You must pass a valid NAL Unit in order to get the correct CMSampleBuffer. + /// - Parameter nalu: NALUnit, Array + /// - Returns: CMSampleBuffer? + public func getCMSampleBuffer(from nalu: NALUnit) -> CMSampleBuffer? { +// print("Read Nalu size \(nalu.count)"); + var mNalu = nalu + let naluType = nalu[4] & 0x1f + + var sampleBuffer: CMSampleBuffer? + switch naluType { + case 0x05: // I frame +// print("Nal type is IDR frame") + guard initialize(SPS: sps, PPS: pps) else { break } + sampleBuffer = decodeToCMSampleBuffer(frame: &mNalu) + + case 0x07: // SPS +// print("Nal type is SPS") + sps = NALUnit(nalu[4...]) + + case 0x08: // PPS +// print("Nal type is PPS") + pps = NALUnit(nalu[4...]) + + default: // B/P Frame +// print("Nal type is B/P frame") + sampleBuffer = decodeToCMSampleBuffer(frame:&mNalu) + } + + return sampleBuffer + } + + + /// Get as many as possible NALU units from stream, incomplete unit will be dropped + /// - Parameter streamBuffer: StreamData, Array + /// - Returns: NALUnit? + public func getNalUnits(streamBuffer: Array) -> NALUnit? { + guard streamBuffer.count != 0 else { return nil } + + //make sure start with start code + if streamBuffer.count < 5 || Array(streamBuffer[0...3]) != startCode { + return nil + } + + var nalUnits = Array() + //find second start code, so startIndex = 4 + var startIndex = 4 + + while ((startIndex + 3) < streamBuffer.count) { + if Array(streamBuffer[startIndex...startIndex+3]) == startCode { + let units = Array(streamBuffer[0.. + /// - Returns: NALUnit? + public func readNalUnits(streamBuffer:inout Array) -> NALUnit? { + guard streamBuffer.count != 0 else { return nil } + + //make sure start with start code + if streamBuffer.count < 5 || Array(streamBuffer[0...3]) != startCode { + return nil + } + + //find second start code, so startIndex = 4 + var startIndex = 4 + + while true { + guard !stop else { return nil } + while ((startIndex + 3) < streamBuffer.count) { + if Array(streamBuffer[startIndex...startIndex+3]) == startCode { + + let units = Array(streamBuffer[0.. CMSampleBuffer? { + guard vtdSession != nil else { return nil } + var bigLen = CFSwapInt32HostToBig(UInt32(frame.count - 4)) + memcpy(&frame, &bigLen, 4) + var blockBuffer: CMBlockBuffer? + var status = CMBlockBufferCreateWithMemoryBlock(allocator: kCFAllocatorDefault, memoryBlock: &frame, blockLength: frame.count, blockAllocator: kCFAllocatorNull, customBlockSource: nil, offsetToData: 0, dataLength: frame.count, flags: 0, blockBufferOut: &blockBuffer) + + guard status == kCMBlockBufferNoErr else { return nil } + + var sampleBuffer: CMSampleBuffer? + let sampleSizeArray = [frame.count] + +// let timing = CMSampleTimingInfo(duration: CMTime(value: 1, timescale: 25), presentationTimeStamp: CMTime(seconds: 1, preferredTimescale: 25), decodeTimeStamp: CMTime.invalid) + + status = CMSampleBufferCreateReady(allocator: kCFAllocatorDefault, dataBuffer: blockBuffer, formatDescription: formatDesc, sampleCount: 1, sampleTimingEntryCount: 0, sampleTimingArray: nil, sampleSizeEntryCount: sampleSizeArray.count, sampleSizeArray: sampleSizeArray, sampleBufferOut: &sampleBuffer) + + guard status == noErr else { return nil } + return sampleBuffer + } + + func initialize(SPS: NALUnit?, PPS: NALUnit?) -> Bool { + guard let SPS = SPS, let PPS = PPS else { return false } + guard createH264FormatDescription(SPS: SPS, PPS: PPS) == noErr else { return false } + guard createVTDecompressionSession() == noErr else { return false } + return true + } + + func createH264FormatDescription(SPS sps: Array, PPS pps: Array) -> OSStatus { + if formatDesc != nil { formatDesc = nil } + + let status = sps.withUnsafeBufferPointer { spsBP -> OSStatus in //<- Specify return type explicitly. + pps.withUnsafeBufferPointer { ppsBP in + let paramSet = [spsBP.baseAddress!, ppsBP.baseAddress!] + let paramSizes = [spsBP.count, ppsBP.count] + return paramSet.withUnsafeBufferPointer { paramSetBP in + paramSizes.withUnsafeBufferPointer { paramSizesBP in + CMVideoFormatDescriptionCreateFromH264ParameterSets(allocator: kCFAllocatorDefault, parameterSetCount: 2, parameterSetPointers: paramSetBP.baseAddress!, parameterSetSizes: paramSizesBP.baseAddress!, nalUnitHeaderLength: 4, formatDescriptionOut: &formatDesc) + } + } + } + } + + return status + } + + func createVTDecompressionSession() -> OSStatus { + guard formatDesc != nil else { return -1 } + + if let session = vtdSession { + let accept = VTDecompressionSessionCanAcceptFormatDescription(session, formatDescription: formatDesc!) + guard !accept else { return 0 } + // if current session cannot aceept the format, invalidate and create a new one + VTDecompressionSessionInvalidate(session) + vtdSession = nil + } + + var decoderParameters: [String: CFBoolean]? + #if os(macOS) + decoderParameters = [String: CFBoolean]() + decoderParameters!.updateValue(kCFBooleanTrue, forKey: kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder as String) + #endif + + var destPBAttributes = [String: UInt32]() + destPBAttributes.updateValue(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, forKey: kCVPixelBufferPixelFormatTypeKey as String) + + // comment below back and pass to VTDecompressionSessionCreate if you don't want to call VTDecompressionSessionDecodeFrame(_:sampleBuffer:flags:infoFlagsOut:outputHandler:) +// var outputCallback = VTDecompressionOutputCallbackRecord() +// outputCallback.decompressionOutputCallback = decodeFrameCallback +// outputCallback.decompressionOutputRefCon = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + + // pass outputCallback above to outputCallback if you need + let status = VTDecompressionSessionCreate(allocator: kCFAllocatorDefault, formatDescription: formatDesc!, decoderSpecification: decoderParameters as CFDictionary?, imageBufferAttributes: destPBAttributes as CFDictionary, outputCallback: nil, decompressionSessionOut: &vtdSession) + + return status + } +} + +func decodeFrameCallback(_ decompressionOutputRefCon: UnsafeMutableRawPointer?, _ sourceFrameRefCon: UnsafeMutableRawPointer?, _ status: OSStatus, _ infoFlags: VTDecodeInfoFlags, _ imageBuffer: CVImageBuffer?, _ presentationTimeStamp: CMTime, _ presentationDuration: CMTime) -> Void { + // only get called if you create VTDecompressionOutputCallbackRecord and pass to VTDecompressionSessionCreate(outputCallback:) + +} + From 66ada098df30b7e31795980e5ad80d3e68573b15 Mon Sep 17 00:00:00 2001 From: Xuan Liu Date: Tue, 21 Apr 2020 12:23:21 +0800 Subject: [PATCH 2/4] fix codedov failure --- TelloSwiftTests/MockTelloFlightTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelloSwiftTests/MockTelloFlightTests.swift b/TelloSwiftTests/MockTelloFlightTests.swift index 288d8db..8ed2c99 100644 --- a/TelloSwiftTests/MockTelloFlightTests.swift +++ b/TelloSwiftTests/MockTelloFlightTests.swift @@ -47,7 +47,7 @@ class MockTelloFlightTests: XCTestCase { e.fulfill() } wait(for: [e], timeout: 5) - tello.invalidate() + tello.clearTimer() XCTAssertNil(tello.kaTimer) } From 194a81d86ddfadb59e2e01fb3eecd804f754ec73 Mon Sep 17 00:00:00 2001 From: Xuan Liu Date: Tue, 21 Apr 2020 12:29:26 +0800 Subject: [PATCH 3/4] update README --- README.md | 2 +- TelloVideoDecoder/README.md | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b81a338..9746c98 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ TelloSwift is built upon SwiftNIO purely in Swift, providing flexible protocols * Event-driven duplex command handlers based on SwiftNIO, powering sync and async command execution; * Many useful built-in control commands to design your own flight flow; * Full coverage for both Tello SDK and Tello 2.0 SDK (except for swarm) +* Provide a default H264 video decoder, more details [here](https://github.com/liuxuan30/TelloSwift/tree/master/TelloVideoDecoder) ## Requirement * Xcode 11 / Swift 5 @@ -95,4 +96,3 @@ It will directly forward the raw stream bytes to the delegate. To turn on the ca ## TODO list 1. Swarm support -2. Better video streaming solution diff --git a/TelloVideoDecoder/README.md b/TelloVideoDecoder/README.md index 37f6de2..e343094 100644 --- a/TelloVideoDecoder/README.md +++ b/TelloVideoDecoder/README.md @@ -1,7 +1,7 @@ # TelloVideoH264Decoder -This contains a demo macOS app to illustrate how to use the TelloVideoH264Decoder +This contains a demo macOS app to illustrate how to use the `TelloVideoH264Decoder` -The TelloVideoH264Decoder serves like a demo decoder that can meet common requirement +The `TelloVideoH264Decoder` serves like a demo decoder that can meet common requirement I'm no expert on video decoding and I merely assemble code from different places and make it work. It could be a little buggy, and not world-class flexible for some configurations (e.g. `decodeFlags`) @@ -10,9 +10,13 @@ Therefore I will be grateful that if you can send me pull requests to make it be ## Get a valid NAL Unit You need to first generate valid NALU either by yourself or by `getNalUnits()`/ `readNalUnits()`. -`getNalUnits(streamBuffer: Array) -> NALUnit?` will get all valid NALU and abandon incomplete NALU (data before next start code) +>getNalUnits(streamBuffer: Array) -> NALUnit? -`readNalUnits(streamBuffer:inout Array) -> NALUnit?` will consume `streamBuffer`. To stop reading, call `stopProcessing()` +will get all valid NALU and abandon incomplete NALU (data before next start code) + +>readNalUnits(streamBuffer:inout Array) -> NALUnit? + +will consume `streamBuffer`. To stop reading, call `stopProcessing()` ## Get CMSampleBuffer from NALU Get a CMSampleBuffer by `getCMSampleBuffer(from nalu: NALUnit)` From 808eece6f46b300b6eec0fab6a8abec00f51acb2 Mon Sep 17 00:00:00 2001 From: Xuan Liu Date: Tue, 21 Apr 2020 13:05:07 +0800 Subject: [PATCH 4/4] add codebeatignore --- .codebeatignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .codebeatignore diff --git a/.codebeatignore b/.codebeatignore new file mode 100644 index 0000000..e794d41 --- /dev/null +++ b/.codebeatignore @@ -0,0 +1 @@ +TelloVideoDecoder/**