From 751a0d532515058bb4684e8afd5b0166f26ba6c5 Mon Sep 17 00:00:00 2001 From: Huiping Guo Date: Sun, 17 Dec 2023 23:43:25 +0900 Subject: [PATCH] add swiftnio implement --- Package.resolved | 32 ++++++++++++ Package.swift | 11 ++-- Sources/HPRTMP/RTMPClient.swift | 93 +++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 Package.resolved create mode 100644 Sources/HPRTMP/RTMPClient.swift diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..85a7d8c --- /dev/null +++ b/Package.resolved @@ -0,0 +1,32 @@ +{ + "pins" : [ + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "cd142fd2f64be2100422d658e7411e39489da985", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "a902f1823a7ff3c9ab2fba0f992396b948eda307", + "version" : "1.0.5" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio", + "state" : { + "revision" : "702cd7c56d5d44eeba73fdf83918339b26dc855c", + "version" : "2.62.0" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 72748a4..1ce242a 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "HPRTMP", - platforms: [.iOS(.v14),.macOS(.v11)], + platforms: [.iOS(.v14), .macOS(.v11)], products: [ .library( name: "HPRTMP", @@ -13,15 +13,14 @@ let package = Package( ), ], dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), + .package(url: "https://github.com/apple/swift-nio", from: "2.0.0"), ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "HPRTMP", - dependencies: []), + dependencies: [ + .product(name: "NIO", package: "swift-nio") + ]), .testTarget( name: "HPRTMPTests", dependencies: ["HPRTMP"]), diff --git a/Sources/HPRTMP/RTMPClient.swift b/Sources/HPRTMP/RTMPClient.swift new file mode 100644 index 0000000..7decb74 --- /dev/null +++ b/Sources/HPRTMP/RTMPClient.swift @@ -0,0 +1,93 @@ +import Foundation +import NIO + +final class RTMPClientHandler: ChannelInboundHandler { + typealias InboundIn = Data + private let responseCallback: (Data) -> Void + + init(responseCallback: @escaping (Data) -> Void) { + self.responseCallback = responseCallback + } + + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + let data = self.unwrapInboundIn(data) + responseCallback(data) + } +} + +final class DataDecoder: ByteToMessageDecoder { + typealias InboundIn = ByteBuffer + typealias InboundOut = Data + + func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { + var data = Data() + buffer.readWithUnsafeReadableBytes { ptr in + data.append(ptr.baseAddress!.assumingMemoryBound(to: UInt8.self), count: ptr.count) + return ptr.count + } + context.fireChannelRead(wrapInboundOut(data)) + return .continue + } +} + +final class DataEncoder: MessageToByteEncoder { + typealias OutboundIn = Data + typealias OutboundOut = ByteBuffer + + func encode(data: Data, out: inout ByteBuffer) throws { + out.writeBytes(data) + } +} + +class RTMPClient { + private let group: EventLoopGroup + private var channel: Channel? + private let host: String + private let port: Int + private var responseCallback: ((Data) -> Void)? + + init(host: String, port: Int) { + self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + self.host = host + self.port = port + } + + func connect(responseCallback: @escaping (Data) -> Void) { + self.responseCallback = responseCallback + let bootstrap = ClientBootstrap(group: group) + .channelInitializer { channel in + channel.pipeline.addHandlers([ + ByteToMessageHandler(DataDecoder()), + MessageToByteHandler(DataEncoder()), + RTMPClientHandler(responseCallback: self.responseReceived) + ]) + } + + do { + self.channel = try bootstrap.connect(host: host, port: port).wait() + print("Connected to \(host):\(port)") + } catch { + print("Failed to connect: \(error)") + } + } + + func send(data: Data) { + guard let channel = channel else { + print("Connection not established") + return + } + channel.writeAndFlush(data, promise: nil) + } + + private func responseReceived(data: Data) { + responseCallback?(data) + } + + func shutdown() { + do { + try group.syncShutdownGracefully() + } catch { + print("Error shutting down: \(error)") + } + } +}