me
me copied to clipboard
Swift & C (Part 1: RTMP Routine)
hello world
先起一个linux c的项目, foo.c
int biubiubiu() { return 666; }
Makefile
libfoo.a: foo.o
libtool -static -o $@ $^
foo.o: foo.c
clang -c -arch arm64 -arch x86_64 -o $@ $<
然后Xcode新建一个SwiftUI或Swift CommandLine都行,打开后在Target/General/Frameworks and Libraries中添加libfoo.a, 几点说明:
- 在Target中添加等同于通过finder将libfoo.a拖动到左侧项目project navigator,这种方式添加的都是引用,如果删除的时候选择move to trash就会将引用的外部文件删掉
- 根据需要选择是否将lib文件复制到项目中,比如要上传到git,那么就需要将libfoo.a放在项目中push到repo,如果另外一边libfoo.a需要同步开发,则ref到linux c的项目更好,同步更新
- libfoo.a是静态库,ld在link时需要找到library,link后不需要library,因为已经嵌入到app中
第二步,在项目中添加一个C File, 此时Xcode会提示你添加一个Bridge文件,选同意,同意后保留Bridge删除刚才的C文件。Bridge就是用来告诉swiftc libfoo.a的definitions,所以可以选择3种方式:
- 去tm的header,直接在bridge中定义
int biubiubiu();
- 在linux c项目中写一个make install将header复制到/usr/local/include, 然后在bridge中
#include <foo/foo.h>
- 将foo.h复制到项目中,然后在bridge中
#include "foo.h"
最后,在swift中可以直接调用:
app.swift
func main() {
print(biubiubiu()) // 666
}
main()
RTMP_LibVersion
- 编译librtmp.a (
gcc -arch arm64 -arch x86_64
), make install - 为了共享给团队小伙伴,复制librtmp.a和header到项目中
- 添加bridge, 在bridge中include librtmp header
func main() {
let version = RTMP_LibVersion();
let versionString = String(format:"0x%08x", version); // RTMP_LibVersion: 0x00020300
print("RTMP_LibVersion: \(versionString)");
RTMP_LogSetLevel(RTMP_LOGALL)
}
main()
RTMP main routine
import Foundation
func main() {
RTMP_LogSetLevel(RTMP_LOGDEBUG)
// Show librtmp version
let version = RTMP_LibVersion();
let versionString = String(format:"0x%08x", version); // RTMP_LibVersion: 0x00020300
print("RTMP_LibVersion: \(versionString)");
// C: RTMP rtmp
var rtmp = RTMP()
RTMP_Init(&rtmp)
// C: char *url = "rtmp://shgbit.xyz/app/1"
// C: RTMP_SetupURL(&rtmp, url)
// RTMP_SetupURL(UnsafeMutablePointer<RTMP>! , _ url: UnsafeMutalbePointer<CChar>!)
let url = "rtmp://shgbit.xyz/app/1"
_ = url.withCString { ptr in
RTMP_SetupURL(&rtmp, UnsafeMutablePointer<CChar>(mutating: ptr))
}
// C: RTMP_EnableWrite(&rtmp)
RTMP_EnableWrite(&rtmp)
// C: RTMP_Connect(&rtmp, NULL)
RTMP_Connect(&rtmp, nil)
// C: RTMP_ConnectStream(&rtmp, NULL)
RTMP_ConnectStream(&rtmp, 0)
// C: RTMP_Close(&rtmp)
RTMP_Close(&rtmp)
}
main()
运行后输出:
RTMP_LibVersion: 0x00020300
DEBUG: Parsing...
DEBUG: Parsed protocol: 0
DEBUG: Parsed host : shgbit.xyz
DEBUG: Parsed app : app
DEBUG: RTMP_Connect1, ... connected, handshaking
DEBUG: HandShake: Type Answer : 03
DEBUG: HandShake: Server Uptime : 1653333607
DEBUG: HandShake: FMS Version : -14.92.-63.-82
DEBUG: RTMP_Connect1, handshaked
DEBUG: Invoking connect
DEBUG: HandleServerBW: server BW = 2500000
DEBUG: HandleClientBW: client BW = 2500000 2
DEBUG: HandleChangeChunkSize, received: chunk size change to 60000
DEBUG: RTMP_ClientPacket, received: invoke 510 bytes
DEBUG: (object begin)
DEBUG: (object begin)
DEBUG: Property: <Name: fmsVer, STRING: FMS/3,5,3,888>
DEBUG: Property: <Name: capabilities, NUMBER: 127.00>
DEBUG: Property: <Name: mode, NUMBER: 1.00>
DEBUG: (object end)
DEBUG: (object begin)
DEBUG: Property: <Name: level, STRING: status>
DEBUG: Property: <Name: code, STRING: NetConnection.Connect.Success>
DEBUG: Property: <Name: description, STRING: Connection succeeded>
DEBUG: Property: <Name: objectEncoding, NUMBER: 0.00>
DEBUG: Property: <Name: data, ECMA_ARRAY>
DEBUG: (object begin)
DEBUG: Property: <Name: version, STRING: 3,5,3,888>
DEBUG: Property: <Name: srs_sig, STRING: SRS>
DEBUG: Property: <Name: srs_server, STRING: SRS/4.0.251(Leo)>
DEBUG: Property: <Name: srs_license, STRING: MIT>
DEBUG: Property: <Name: srs_url, STRING: https://github.com/ossrs/srs>
DEBUG: Property: <Name: srs_version, STRING: 4.0.251>
DEBUG: Property: <Name: srs_authors, STRING: https://github.com/ossrs/srs/blob/4.0release/trunk/AUTHORS.txt>
DEBUG: Property: <Name: srs_server_ip, STRING: 172.18.0.2>
DEBUG: Property: <Name: srs_pid, NUMBER: 1.00>
DEBUG: Property: <Name: srs_id, STRING: c9615dg9>
DEBUG: (object end)
DEBUG: (object end)
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <_result>
DEBUG: HandleInvoke, received result for method call <connect>
DEBUG: Invoking releaseStream
DEBUG: Invoking FCPublish
DEBUG: Invoking createStream
DEBUG: RTMP_ClientPacket, received: invoke 21 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <onBWDone>
DEBUG: Invoking _checkbw
DEBUG: RTMP_ClientPacket, received: invoke 21 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: Property: NULL
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <_result>
DEBUG: HandleInvoke, received result id 2.000000 without matching request
DEBUG: RTMP_ClientPacket, received: invoke 21 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: Property: NULL
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <_result>
DEBUG: HandleInvoke, received result id 3.000000 without matching request
DEBUG: RTMP_ClientPacket, received: invoke 29 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <_result>
DEBUG: HandleInvoke, received result for method call <createStream>
DEBUG: Invoking publish
DEBUG: RTMP_ClientPacket, received: invoke 102 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: (object begin)
DEBUG: Property: <Name: code, STRING: NetStream.Publish.Start>
DEBUG: Property: <Name: description, STRING: Started publishing stream.>
DEBUG: (object end)
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <onFCPublish>
DEBUG: RTMP_ClientPacket, received: invoke 136 bytes
DEBUG: (object begin)
DEBUG: Property: NULL
DEBUG: (object begin)
DEBUG: Property: <Name: level, STRING: status>
DEBUG: Property: <Name: code, STRING: NetStream.Publish.Start>
DEBUG: Property: <Name: description, STRING: Started publishing stream.>
DEBUG: Property: <Name: clientid, STRING: ASAICiss>
DEBUG: (object end)
DEBUG: (object end)
DEBUG: HandleInvoke, server invoking <onStatus>
DEBUG: HandleInvoke, onStatus: NetStream.Publish.Start
DEBUG: Invoking FCUnpublish
DEBUG: Invoking deleteStream
AMF
func amf() {
var data: Array<CChar> = Array(repeating: 0x00, count: 256)
let _ = data.withUnsafeMutableBufferPointer { ptr in
var enc: UnsafeMutablePointer<CChar>
let end = ptr.baseAddress?.advanced(by: ptr.count)
enc = AMF_EncodeBoolean(ptr.baseAddress, end, 1)
enc = AMF_EncodeInt32(enc, end, 7)
"nonocast".withCString { cstr in
var val = AVal(av_val: UnsafeMutablePointer<CChar>(mutating: cstr), av_len: Int32(strlen(cstr)))
enc = AMF_EncodeString(enc, end, &val)
}
let offset = enc - ptr.baseAddress!
RTMP_LogHexString(Int32(RTMP_LOGDEBUG.rawValue), ptr.baseAddress, UInt(offset))
}
}
输出结果如下:
DEBUG: 0000: 01 01 00 00 00 07 02 00 08 6e 6f 6e 6f 63 61 73 .........nonocas
DEBUG: 0010: 74
参考阅读
- swift-rtmp/Rtmp.swift at master · kphrx/swift-rtmp
- Using Imported C Functions in Swift | Apple Developer Documentation
- Using Imported C Structs and Unions in Swift | Apple Developer Documentation
- How to call C code from Swift - The.Swift.Dev.
- Swift和C编程 | Swift 和 C 不得不说的故事 | Swift 教程 - Swift 语言学习 - Swift code - SwiftGG 翻译组 - 高质量的 Swift 译文网站