@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/README.md +112 -4
- package/android/src/main/java/com/reactnativeivsbroadcast/IVSBroadcastModule.kt +393 -0
- package/ios/IVSBroadcastModule.m +554 -1
- package/lib/index.d.ts +75 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +199 -0
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +86 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +233 -6
- package/src/types.ts +118 -1
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
|
-
|
|
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
|
-
|
|
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(
|
|
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";
|