@abdurrahman-dev/react-native-ivs-broadcast 0.2.5 → 0.2.6

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.
package/src/index.ts CHANGED
@@ -10,6 +10,12 @@ import type {
10
10
  VideoStats,
11
11
  VideoConfig,
12
12
  AudioConfig,
13
+ TransmissionStatistics,
14
+ AudioDeviceStats,
15
+ NetworkTestResult,
16
+ DeviceDescriptor,
17
+ CameraCapabilities,
18
+ TimedMetadata,
13
19
  } from "./types";
14
20
 
15
21
  const { IVSBroadcastModule } = NativeModules;
@@ -21,7 +27,7 @@ if (!IVSBroadcastModule) {
21
27
  if (__DEV__) {
22
28
  console.warn(
23
29
  "IVSBroadcastModule native module is not available. " +
24
- "Make sure you have properly linked the module and rebuilt the app with 'npx expo run:ios'."
30
+ "Make sure you have properly linked the module and rebuilt the app with 'npx expo run:ios'."
25
31
  );
26
32
  }
27
33
  // Modül yoksa eventEmitter'ı null bırak, hata fırlatma
@@ -39,7 +45,7 @@ class IVSBroadcast {
39
45
  if (!IVSBroadcastModule) {
40
46
  throw new Error(
41
47
  "IVSBroadcastModule native module is not available. " +
42
- "This module requires a development build. Please run 'npx expo run:ios' to rebuild the app."
48
+ "This module requires a development build. Please run 'npx expo run:ios' to rebuild the app."
43
49
  );
44
50
  }
45
51
  }
@@ -51,7 +57,7 @@ class IVSBroadcast {
51
57
  config: IVSBroadcastConfig
52
58
  ): Promise<IVSBroadcastSession> {
53
59
  this.checkModuleAvailable();
54
-
60
+
55
61
  if (!config.rtmpUrl) {
56
62
  throw new Error("RTMP URL is required");
57
63
  }
@@ -163,6 +169,34 @@ class IVSBroadcast {
163
169
  }
164
170
  }
165
171
 
172
+ /**
173
+ * Kamera listesinden belirli bir kamerayı seçer
174
+ * @param sessionId - Broadcast session ID
175
+ * @param deviceId - Seçilecek kameranın deviceId'si (listAvailableDevices'dan alınabilir)
176
+ */
177
+ async selectCamera(sessionId: string, deviceId: string): Promise<void> {
178
+ this.checkModuleAvailable();
179
+ try {
180
+ await IVSBroadcastModule.selectCamera(sessionId, deviceId);
181
+ } catch (error: any) {
182
+ throw new Error(`Failed to select camera: ${error.message}`);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Mikrofon listesinden belirli bir mikrofonu seçer
188
+ * @param sessionId - Broadcast session ID
189
+ * @param deviceId - Seçilecek mikrofonun deviceId'si (listAvailableDevices'dan alınabilir)
190
+ */
191
+ async selectMicrophone(sessionId: string, deviceId: string): Promise<void> {
192
+ this.checkModuleAvailable();
193
+ try {
194
+ await IVSBroadcastModule.selectMicrophone(sessionId, deviceId);
195
+ } catch (error: any) {
196
+ throw new Error(`Failed to select microphone: ${error.message}`);
197
+ }
198
+ }
199
+
166
200
  /**
167
201
  * Mikrofonu açıp kapatır
168
202
  */
@@ -217,6 +251,185 @@ class IVSBroadcast {
217
251
  }
218
252
  }
219
253
 
254
+ /**
255
+ * Kullanılabilir cihazları listeler
256
+ */
257
+ async listAvailableDevices(): Promise<DeviceDescriptor[]> {
258
+ this.checkModuleAvailable();
259
+ try {
260
+ return await IVSBroadcastModule.listAvailableDevices();
261
+ } catch (error: any) {
262
+ throw new Error(`Failed to list available devices: ${error.message}`);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Bağlı cihazları listeler
268
+ */
269
+ async listAttachedDevices(sessionId: string): Promise<DeviceDescriptor[]> {
270
+ this.checkModuleAvailable();
271
+ try {
272
+ return await IVSBroadcastModule.listAttachedDevices(sessionId);
273
+ } catch (error: any) {
274
+ throw new Error(`Failed to list attached devices: ${error.message}`);
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Kamera zoom seviyesini ayarlar
280
+ */
281
+ async setCameraZoom(sessionId: string, zoomFactor: number): Promise<void> {
282
+ this.checkModuleAvailable();
283
+ try {
284
+ await IVSBroadcastModule.setCameraZoom(sessionId, zoomFactor);
285
+ } catch (error: any) {
286
+ throw new Error(`Failed to set camera zoom: ${error.message}`);
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Kamera flaşını açıp kapatır
292
+ */
293
+ async setTorchEnabled(sessionId: string, enabled: boolean): Promise<void> {
294
+ this.checkModuleAvailable();
295
+ try {
296
+ await IVSBroadcastModule.setTorchEnabled(sessionId, enabled);
297
+ } catch (error: any) {
298
+ throw new Error(`Failed to set torch: ${error.message}`);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Kamera yeteneklerini alır
304
+ */
305
+ async getCameraCapabilities(sessionId: string): Promise<CameraCapabilities> {
306
+ this.checkModuleAvailable();
307
+ try {
308
+ return await IVSBroadcastModule.getCameraCapabilities(sessionId);
309
+ } catch (error: any) {
310
+ throw new Error(`Failed to get camera capabilities: ${error.message}`);
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Zamanlı metadata gönderir
316
+ */
317
+ async sendTimedMetadata(sessionId: string, metadata: string): Promise<void> {
318
+ this.checkModuleAvailable();
319
+ try {
320
+ await IVSBroadcastModule.sendTimedMetadata(sessionId, metadata);
321
+ } catch (error: any) {
322
+ throw new Error(`Failed to send timed metadata: ${error.message}`);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Ağ kalite testi başlatır
328
+ */
329
+ async startNetworkTest(
330
+ rtmpUrl: string,
331
+ streamKey?: string,
332
+ duration?: number
333
+ ): Promise<string> {
334
+ this.checkModuleAvailable();
335
+ try {
336
+ return await IVSBroadcastModule.startNetworkTest(
337
+ rtmpUrl,
338
+ streamKey,
339
+ duration
340
+ );
341
+ } catch (error: any) {
342
+ throw new Error(`Failed to start network test: ${error.message}`);
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Ağ kalite testini iptal eder
348
+ */
349
+ async cancelNetworkTest(testId: string): Promise<void> {
350
+ this.checkModuleAvailable();
351
+ try {
352
+ await IVSBroadcastModule.cancelNetworkTest(testId);
353
+ } catch (error: any) {
354
+ throw new Error(`Failed to cancel network test: ${error.message}`);
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Ses gain seviyesini ayarlar (0.0 - 2.0 arası)
360
+ */
361
+ async setAudioGain(sessionId: string, gain: number): Promise<void> {
362
+ this.checkModuleAvailable();
363
+ try {
364
+ await IVSBroadcastModule.setAudioGain(sessionId, gain);
365
+ } catch (error: any) {
366
+ throw new Error(`Failed to set audio gain: ${error.message}`);
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Mevcut ses gain seviyesini alır
372
+ */
373
+ async getAudioGain(sessionId: string): Promise<number> {
374
+ this.checkModuleAvailable();
375
+ try {
376
+ return await IVSBroadcastModule.getAudioGain(sessionId);
377
+ } catch (error: any) {
378
+ throw new Error(`Failed to get audio gain: ${error.message}`);
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Picture-in-Picture modunu başlatır (iOS 15+, Android 8.0+)
384
+ * @param sessionId - Broadcast session ID
385
+ */
386
+ async startPictureInPicture(sessionId: string): Promise<void> {
387
+ this.checkModuleAvailable();
388
+ try {
389
+ await IVSBroadcastModule.startPictureInPicture(sessionId);
390
+ } catch (error: any) {
391
+ throw new Error(`Failed to start Picture-in-Picture: ${error.message}`);
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Picture-in-Picture modunu durdurur
397
+ * @param sessionId - Broadcast session ID
398
+ */
399
+ async stopPictureInPicture(sessionId: string): Promise<void> {
400
+ this.checkModuleAvailable();
401
+ try {
402
+ await IVSBroadcastModule.stopPictureInPicture(sessionId);
403
+ } catch (error: any) {
404
+ throw new Error(`Failed to stop Picture-in-Picture: ${error.message}`);
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Picture-in-Picture durumunu alır
410
+ * @param sessionId - Broadcast session ID
411
+ */
412
+ async getPictureInPictureState(sessionId: string): Promise<string> {
413
+ this.checkModuleAvailable();
414
+ try {
415
+ return await IVSBroadcastModule.getPictureInPictureState(sessionId);
416
+ } catch (error: any) {
417
+ throw new Error(`Failed to get Picture-in-Picture state: ${error.message}`);
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Picture-in-Picture desteğinin olup olmadığını kontrol eder
423
+ */
424
+ async isPictureInPictureSupported(): Promise<boolean> {
425
+ this.checkModuleAvailable();
426
+ try {
427
+ return await IVSBroadcastModule.isPictureInPictureSupported();
428
+ } catch (error: any) {
429
+ return false;
430
+ }
431
+ }
432
+
220
433
  /**
221
434
  * Event listener ekler
222
435
  * @returns Cleanup fonksiyonu - listener'ı kaldırmak için çağırılır
@@ -241,9 +454,23 @@ class IVSBroadcast {
241
454
  eventType: "onVideoStats",
242
455
  callback: (stats: VideoStats) => void
243
456
  ): () => void;
457
+ addListener(
458
+ eventType: "onTransmissionStatistics",
459
+ callback: (stats: TransmissionStatistics) => void
460
+ ): () => void;
461
+ addListener(
462
+ eventType: "onAudioDeviceStats",
463
+ callback: (stats: AudioDeviceStats) => void
464
+ ): () => void;
465
+ addListener(
466
+ eventType: "onNetworkTestResult",
467
+ callback: (result: NetworkTestResult) => void
468
+ ): () => void;
244
469
  addListener(eventType: string, callback: (data: any) => void): () => void {
245
470
  if (!eventEmitter) {
246
- console.warn("EventEmitter is not available. Native module may not be linked.");
471
+ console.warn(
472
+ "EventEmitter is not available. Native module may not be linked."
473
+ );
247
474
  // Modül yoksa boş bir cleanup fonksiyonu döndür
248
475
  return () => {};
249
476
  }
@@ -278,7 +505,7 @@ class IVSBroadcast {
278
505
  */
279
506
  removeListener(eventType: string, callback?: (data: any) => void): void {
280
507
  if (!eventEmitter) return;
281
-
508
+
282
509
  const callbacks = this.listeners.get(eventType);
283
510
  if (callbacks) {
284
511
  if (callback) {
@@ -298,7 +525,7 @@ class IVSBroadcast {
298
525
  */
299
526
  removeAllListeners(eventType?: string): void {
300
527
  if (!eventEmitter) return;
301
-
528
+
302
529
  if (eventType) {
303
530
  this.listeners.delete(eventType);
304
531
  eventEmitter.removeAllListeners(eventType);
package/src/types.ts CHANGED
@@ -55,6 +55,11 @@ export interface PreviewViewProps {
55
55
  */
56
56
  isMirrored?: boolean;
57
57
 
58
+ /**
59
+ * Picture-in-Picture konfigürasyonu
60
+ */
61
+ pictureInPicture?: PictureInPictureConfig;
62
+
58
63
  /**
59
64
  * View stili
60
65
  */
@@ -71,6 +76,18 @@ export interface PreviewViewRef {
71
76
  * Preview'ı yeniden yükle
72
77
  */
73
78
  refresh: () => void;
79
+ /**
80
+ * Picture-in-Picture modunu başlat
81
+ */
82
+ startPictureInPicture?: () => Promise<void>;
83
+ /**
84
+ * Picture-in-Picture modunu durdur
85
+ */
86
+ stopPictureInPicture?: () => Promise<void>;
87
+ /**
88
+ * Picture-in-Picture durumunu al
89
+ */
90
+ getPictureInPictureState?: () => Promise<PictureInPictureState>;
74
91
  }
75
92
 
76
93
  export type BroadcastEventType =
@@ -78,7 +95,9 @@ export type BroadcastEventType =
78
95
  | "onError"
79
96
  | "onNetworkHealth"
80
97
  | "onAudioStats"
81
- | "onVideoStats";
98
+ | "onVideoStats"
99
+ | "onTransmissionStatistics"
100
+ | "onAudioDeviceStats";
82
101
 
83
102
  export interface BroadcastEvent {
84
103
  type: BroadcastEventType;
@@ -103,3 +122,101 @@ export interface VideoStats {
103
122
  width: number;
104
123
  height: number;
105
124
  }
125
+
126
+ // Gelişmiş İstatistikler
127
+ export interface TransmissionStatistics {
128
+ /** Ölçülen ortalama gönderme bitrate'i */
129
+ measuredBitrate: number;
130
+ /** SDK tarafından önerilen bitrate */
131
+ recommendedBitrate: number;
132
+ /** Ortalama round trip time (ms) */
133
+ rtt: number;
134
+ /** Yayın kalitesi */
135
+ broadcastQuality: BroadcastQuality;
136
+ /** Ağ sağlığı */
137
+ networkHealth: NetworkHealthLevel;
138
+ }
139
+
140
+ export type BroadcastQuality =
141
+ | "nearMaximum"
142
+ | "high"
143
+ | "medium"
144
+ | "low"
145
+ | "nearMinimum";
146
+
147
+ export type NetworkHealthLevel =
148
+ | "excellent"
149
+ | "high"
150
+ | "medium"
151
+ | "low"
152
+ | "bad";
153
+
154
+ // Ses Cihazı İstatistikleri
155
+ export interface AudioDeviceStats {
156
+ /** Ses peak seviyesi (dBFS, -100 ile 0 arası) */
157
+ peak: number;
158
+ /** Ses RMS seviyesi (dBFS, -100 ile 0 arası) */
159
+ rms: number;
160
+ }
161
+
162
+ // Ağ Kalite Testi
163
+ export interface NetworkTestResult {
164
+ /** Test ilerleme durumu (0-1 arası) */
165
+ progress: number;
166
+ /** Önerilen video konfigürasyonları */
167
+ recommendations: VideoConfig[];
168
+ /** Test durumu */
169
+ status: NetworkTestStatus;
170
+ /** Hata varsa */
171
+ error?: string;
172
+ }
173
+
174
+ export type NetworkTestStatus = "connecting" | "testing" | "success" | "error";
175
+
176
+ // Cihaz Bilgileri
177
+ export interface DeviceDescriptor {
178
+ /** Cihaz tipi */
179
+ type: DeviceType;
180
+ /** Cihaz pozisyonu (kameralar için) */
181
+ position?: DevicePosition;
182
+ /** Cihaz ID'si */
183
+ deviceId: string;
184
+ /** Kullanıcı dostu isim */
185
+ friendlyName: string;
186
+ /** Varsayılan cihaz mı */
187
+ isDefault: boolean;
188
+ }
189
+
190
+ export type DeviceType = "camera" | "microphone" | "userVideo" | "userAudio";
191
+ export type DevicePosition = "front" | "back" | "unknown";
192
+
193
+ // Kamera Özellikleri
194
+ export interface CameraCapabilities {
195
+ /** Minimum zoom faktörü */
196
+ minZoomFactor: number;
197
+ /** Maximum zoom faktörü */
198
+ maxZoomFactor: number;
199
+ /** Flaş desteği var mı */
200
+ isTorchSupported: boolean;
201
+ }
202
+
203
+ // Zamanlı Metadata
204
+ export interface TimedMetadata {
205
+ /** Metadata içeriği */
206
+ content: string;
207
+ /** Gönderim zamanı (opsiyonel) */
208
+ timestamp?: number;
209
+ }
210
+
211
+ // Picture-in-Picture
212
+ export interface PictureInPictureConfig {
213
+ /** PiP modunu etkinleştir */
214
+ enabled?: boolean;
215
+ /** PiP boyutları (iOS için) */
216
+ aspectRatio?: {
217
+ width: number;
218
+ height: number;
219
+ };
220
+ }
221
+
222
+ export type PictureInPictureState = "idle" | "starting" | "active" | "stopping" | "stopped";