blix-expo-settings 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,10 +8,15 @@ public class ExpoSettingsModule: Module {
8
8
  private var rtmpStream: RTMPStream?
9
9
  private var currentStreamStatus: String = "stopped"
10
10
 
11
+
11
12
  public func definition() -> ModuleDefinition {
12
13
  Name("ExpoSettings")
13
14
 
14
- View(ExpoSettingsView.self) {}
15
+ // Registra o view component para o admin se enxergar na live
16
+
17
+ View(ExpoSettingsView.self) {
18
+ // não precisa colocar nada aqui se você não tiver Props
19
+ }
15
20
 
16
21
  Events("onStreamStatus")
17
22
 
@@ -20,165 +25,218 @@ public class ExpoSettingsModule: Module {
20
25
  }
21
26
 
22
27
  Function("initializePreview") { () -> Void in
23
- self.setStatus("previewInitializing")
24
-
25
- self.configureAudioSession()
26
-
27
- let connection = RTMPConnection()
28
- self.rtmpConnection = connection
29
-
30
- let stream = RTMPStream(connection: connection)
31
- self.rtmpStream = stream
32
-
33
- self.configureStream(stream)
34
- self.attachAudioIfAvailable(stream)
35
- self.attachFrontCamera(stream)
36
- self.attachPreviewIfAvailable(stream)
37
-
38
- self.setStatus("previewReady")
39
- }
40
-
41
- Function("publishStream") { (url: String, streamKey: String) -> Void in
42
- self.setStatus("connecting")
43
- print("[ExpoSettings] Publishing to: \(url) key: \(streamKey)")
44
-
45
- // Se não existe stream/connection, cria e aplica TODA a config
46
- if self.rtmpConnection == nil || self.rtmpStream == nil {
28
+ Task {
29
+ self.currentStreamStatus = "previewInitializing"
30
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
31
+
32
+ do {
33
+
34
+ // 0) Configura e ativa o AVAudioSession
35
+ let session = AVAudioSession.sharedInstance()
36
+ do {
37
+ try session.setCategory(.playAndRecord,
38
+ mode: .default,
39
+ options: [.defaultToSpeaker, .allowBluetooth])
40
+ try session.setActive(true)
41
+ } catch {
42
+ print("[ExpoSettings] AVAudioSession error:", error)
43
+ }
44
+
45
+ // 1) Conectar ao servidor RTMP, mas não publica
47
46
  let connection = RTMPConnection()
48
47
  self.rtmpConnection = connection
49
48
 
49
+ // 2) Criar RTMPStream, mas não publica pro servidor ainda
50
50
  let stream = RTMPStream(connection: connection)
51
51
  self.rtmpStream = stream
52
-
53
- self.configureStream(stream)
54
- self.attachAudioIfAvailable(stream)
55
- self.attachFrontCamera(stream)
56
- self.attachPreviewIfAvailable(stream)
52
+ print("[ExpoSettings] RTMPStream initialized")
53
+
54
+ // 3) Configurar captura: frame rate e preset
55
+ stream.sessionPreset = .hd1280x720
56
+ stream.frameRate = 30
57
+ stream.videoOrientation = .portrait
58
+ stream.configuration { captureSession in
59
+ captureSession.automaticallyConfiguresApplicationAudioSession = true
60
+ }
61
+
62
+ // 4) Configurar áudio: anexa microfone
63
+ if let audioDevice = AVCaptureDevice.default(for: .audio) {
64
+ print("[ExpoSettings] Attaching audio device")
65
+ stream.attachAudio(audioDevice)
66
+ } else {
67
+ print("[ExpoSettings] No audio device found")
68
+ }
69
+
70
+ // 5) Configurar vídeo: anexa câmera frontal
71
+ if let camera = AVCaptureDevice.default(.builtInWideAngleCamera,
72
+ for: .video,
73
+ position: .front) {
74
+ print("[ExpoSettings] Attaching camera device")
75
+ stream.attachCamera(camera) { videoUnit, error in
76
+ guard let unit = videoUnit else {
77
+ print("[ExpoSettings] attachCamera error:", error?.localizedDescription ?? "unknown")
78
+ return
79
+ }
80
+ unit.isVideoMirrored = true
81
+ unit.videoOrientation = .portrait
82
+ unit.preferredVideoStabilizationMode = .standard
83
+ unit.colorFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
84
+
85
+ }
86
+ if let preview = await ExpoSettingsView.current {
87
+ print("[ExpoSettings] Attaching stream to preview view")
88
+ await preview.attachStream(stream)
89
+ } else {
90
+ print("[ExpoSettings] ERROR: Preview view not found!")
91
+ }
92
+ } else {
93
+ print("[ExpoSettings] No camera device found")
94
+ }
95
+
96
+ //6) Definir configurações de codec
97
+ print("[ExpoSettings] Setting audio and video codecs")
98
+ var audioSettings = AudioCodecSettings()
99
+ audioSettings.bitRate = 128 * 1000
100
+ stream.audioSettings = audioSettings
101
+
102
+ let videoSettings = VideoCodecSettings(
103
+ videoSize: .init(width: 720, height: 1280),
104
+ bitRate: 4000 * 1000,
105
+ profileLevel: kVTProfileLevel_H264_Baseline_3_1 as String,
106
+ scalingMode: .trim,
107
+ bitRateMode: .average,
108
+ maxKeyFrameIntervalDuration: 2,
109
+ allowFrameReordering: nil,
110
+ isHardwareEncoderEnabled: true
111
+ )
112
+ stream.videoSettings = videoSettings
57
113
  }
58
-
59
- self.rtmpConnection?.connect(url)
60
- self.setStatus("connected")
61
-
62
- self.setStatus("publishing")
63
- self.rtmpStream?.publish(streamKey)
64
-
65
- self.setStatus("started")
66
- }
67
-
68
- Function("stopStream") { () -> Void in
69
- print("[ExpoSettings] stopStream called")
70
-
71
- if let stream = self.rtmpStream {
72
- stream.close()
73
- stream.attachCamera(nil)
74
- stream.attachAudio(nil)
114
+ self.currentStreamStatus = "previewReady"
115
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
75
116
  }
117
+ }
76
118
 
77
- if let connection = self.rtmpConnection {
78
- connection.close()
119
+ Function("publishStream") { (url: String, streamKey: String) -> Void in
120
+ Task {
121
+
122
+ print("[ExpoSettings] Publishing stream to URL: \(url) with key: \(streamKey)")
123
+
124
+ self.currentStreamStatus = "connecting"
125
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
126
+
127
+ // se não houve initializePreview→recria a connection
128
+ if self.rtmpConnection == nil || self.rtmpStream == nil {
129
+ print("[ExpoSettings] WARNING: Connection or stream not initialized, creating new ones")
130
+ // Create new connection
131
+ let connection = RTMPConnection()
132
+ self.rtmpConnection = connection
133
+
134
+ // Create new stream
135
+ let stream = RTMPStream(connection: connection)
136
+ self.rtmpStream = stream
137
+
138
+ // Captura: preset antes do FPS + orientação no stream
139
+ stream.sessionPreset = .hd1280x720
140
+ stream.frameRate = 30
141
+ stream.videoOrientation = .portrait
142
+ stream.configuration { captureSession in
143
+ captureSession.automaticallyConfiguresApplicationAudioSession = true
144
+ }
145
+
146
+ // Áudio
147
+ if let audioDevice = AVCaptureDevice.default(for: .audio) {
148
+ stream.attachAudio(audioDevice)
149
+ }
150
+
151
+ if let camera = AVCaptureDevice.default(.builtInWideAngleCamera,
152
+ for: .video,
153
+ position: .front) {
154
+ stream.attachCamera(camera) { videoUnit, error in
155
+ guard let unit = videoUnit else {
156
+ print("[ExpoSettings] attachCamera error:", error?.localizedDescription ?? "unknown")
157
+ return
158
+ }
159
+ unit.isVideoMirrored = true
160
+ unit.videoOrientation = .portrait
161
+ unit.preferredVideoStabilizationMode = .standard
162
+ unit.colorFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
163
+ }
164
+ }
165
+
166
+ // Attach preview (se existir)
167
+ if let preview = await ExpoSettingsView.current {
168
+ await preview.attachStream(stream)
169
+ } else {
170
+ print("[ExpoSettings] ERROR: Preview view not found during publish!")
171
+ }
172
+
173
+ var audioSettings = AudioCodecSettings()
174
+ audioSettings.bitRate = 128 * 1000 // 128 kbps
175
+ stream.audioSettings = audioSettings
176
+
177
+ // Vídeo
178
+ let videoSettings = VideoCodecSettings(
179
+ videoSize: .init(width: 720, height: 1280),
180
+ bitRate: 4000 * 1000, // 4 Mbps
181
+ profileLevel: kVTProfileLevel_H264_Baseline_3_1 as String,
182
+ scalingMode: .trim,
183
+ bitRateMode: .average,
184
+ maxKeyFrameIntervalDuration: 2,
185
+ allowFrameReordering: nil,
186
+ isHardwareEncoderEnabled: true
187
+ )
188
+ stream.videoSettings = videoSettings
189
+
190
+ self.currentStreamStatus = "previewReady"
191
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
192
+
193
+ connection.connect(url)
194
+ } else {
195
+ // Use existing connection
196
+ self.rtmpConnection?.connect(url)
197
+ }
198
+ self.currentStreamStatus = "connected"
199
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
200
+
201
+ self.currentStreamStatus = "publishing"
202
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
203
+
204
+ self.rtmpStream?.publish(streamKey)
205
+ print("[ExpoSettings] Stream published successfully")
206
+
207
+ self.currentStreamStatus = "started"
208
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
79
209
  }
80
-
81
- self.rtmpStream = nil
82
- self.rtmpConnection = nil
83
-
84
- self.setStatus("stopped")
85
210
  }
86
- }
87
211
 
88
- // MARK: - Internals
89
-
90
- private func setStatus(_ status: String) {
91
- self.currentStreamStatus = status
92
- sendEvent("onStreamStatus", ["status": status])
93
- }
94
-
95
- private func configureAudioSession() {
96
- let session = AVAudioSession.sharedInstance()
97
- do {
98
- try session.setCategory(
99
- .playAndRecord,
100
- mode: .default,
101
- options: [.defaultToSpeaker, .allowBluetooth]
102
- )
103
- try session.setActive(true)
104
- } catch {
105
- print("[ExpoSettings] AVAudioSession error:", error)
106
- }
107
- }
212
+ Function("stopStream") { () -> Void in
213
+ Task {
214
+ print("[ExpoSettings] stopStream called")
108
215
 
109
- private func configureStream(_ stream: RTMPStream) {
110
- print("[ExpoSettings] Configuring stream...")
111
-
112
- // 1) Captura previsível (16:9)
113
- stream.sessionPreset = .hd1280x720
114
- stream.frameRate = 30
115
-
116
- // 2) Orientação no nível do stream (governa pipeline)
117
- stream.videoOrientation = .portrait
118
-
119
- // 3) Áudio
120
- var audioSettings = AudioCodecSettings()
121
- audioSettings.bitRate = 128 * 1000
122
- stream.audioSettings = audioSettings
123
-
124
- // 4) Vídeo (9:16). Use 720x1280 para estabilidade
125
- let videoSettings = VideoCodecSettings(
126
- videoSize: .init(width: 720, height: 1280),
127
- bitRate: 4_000 * 1000,
128
- profileLevel: kVTProfileLevel_H264_Baseline_3_1 as String,
129
- scalingMode: .trim, // sem distorção (corta). Alternativa: .letterbox (barras)
130
- bitRateMode: .average,
131
- maxKeyFrameIntervalDuration: 2,
132
- allowFrameReordering: nil,
133
- isHardwareEncoderEnabled: true
134
- )
135
- stream.videoSettings = videoSettings
136
-
137
- print("[ExpoSettings] Stream configured preset=\(stream.sessionPreset.rawValue) fps=\(stream.frameRate)")
138
- print("[ExpoSettings] Target=\(Int(videoSettings.videoSize.width))x\(Int(videoSettings.videoSize.height)) orientation=portrait")
139
- }
216
+ // Primeiro pare a publicação (se estiver publicando)
217
+ if let stream = self.rtmpStream {
218
+ print("[ExpoSettings] Stopping stream publication")
219
+ stream.close()
140
220
 
141
- private func attachAudioIfAvailable(_ stream: RTMPStream) {
142
- if let audioDevice = AVCaptureDevice.default(for: .audio) {
143
- print("[ExpoSettings] Attaching audio")
144
- stream.attachAudio(audioDevice)
145
- } else {
146
- print("[ExpoSettings] No audio device found")
147
- }
148
- }
221
+ // Desanexa a câmera e o áudio para liberar recursos
222
+ stream.attachCamera(nil)
223
+ stream.attachAudio(nil)
224
+ }
149
225
 
150
- private func attachFrontCamera(_ stream: RTMPStream) {
151
- guard let camera = AVCaptureDevice.default(
152
- .builtInWideAngleCamera,
153
- for: .video,
154
- position: .front
155
- ) else {
156
- print("[ExpoSettings] No front camera found")
157
- return
158
- }
226
+ // Depois feche a conexão RTMP
227
+ if let connection = self.rtmpConnection {
228
+ print("[ExpoSettings] Closing RTMP connection")
229
+ connection.close()
230
+ }
159
231
 
160
- print("[ExpoSettings] Attaching front camera")
232
+ // Limpe as referências
233
+ self.rtmpStream = nil
234
+ self.rtmpConnection = nil
161
235
 
162
- stream.attachCamera(camera) { videoUnit, error in
163
- guard let unit = videoUnit else {
164
- print("[ExpoSettings] attachCamera error:", error?.localizedDescription ?? "unknown")
165
- return
166
- }
167
-
168
- unit.isVideoMirrored = true
169
- unit.videoOrientation = .portrait // <-- GARANTA que está .portrait (não .po)
170
- unit.preferredVideoStabilizationMode = .standard
171
- unit.colorFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
172
- }
173
- }
236
+ print("[ExpoSettings] Stream and connection closed and resources released")
174
237
 
175
- private func attachPreviewIfAvailable(_ stream: RTMPStream) {
176
- DispatchQueue.main.async {
177
- if let preview = ExpoSettingsView.current {
178
- print("[ExpoSettings] Attaching stream to preview")
179
- preview.attachStream(stream)
180
- } else {
181
- print("[ExpoSettings] Preview not available yet")
238
+ self.currentStreamStatus = "stopped"
239
+ sendEvent("onStreamStatus", ["status": self.currentStreamStatus])
182
240
  }
183
241
  }
184
242
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blix-expo-settings",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "LiveStream",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",